Java四种引用使用详解

为什么要定义四种引用?

一、让程序员可以通过代码的方式决定某些对象的生命周期;

二、有利于JVM进行垃圾回收。

强引用

创建一个对象并把对象赋给一个引用变量

强引用有引用变量指向时永远不会被垃圾回收,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。永远都不会被回收

public class Test {
    public static void main(String[] args) {
        new Test().fun1();
    }

    public void fun1() {
        Object object = new Object();
        Object[] objArr = new Object[1000];
    }
}

在运行Object[] objArr = new Object[1000];这一句时,就算JVM抛出OOM异常,也不会被回收。

不过在运行完fun1函数后,因为object和objArr
两个引用变量不存在了,他们指向的对象都会在某个时候被JVM回收

PS:

String str ="hello";就算str不存在了,”hello”不会被回收,因为”hello”位于常量区,而JVM垃圾回收的是堆区内存。

软引用(SoftReference):

如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它。

如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。

软引用可用来实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄露,增强程序的健壮性。

SoftReference的特点是它的一个实例保存对一个Java对象的软引用, 该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。

也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对 这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。 另外,一旦垃圾线程回收该Java对象之 后,get()方法将返回null。

MyObject aRef = new  MyObject();  
SoftReference aSoftRef=new SoftReference(aRef); 
aRef = null;
MyObject anotherRef=(MyObject)aSoftRef.get(); 

代码的前两句,因为有强引用的存在,所以是强可及对象,不会被回收,在执行aRef=null后,就只剩软引用了,软可及对象会在内存不足的时候被回收。软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。

作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法,如:

ReferenceQueue queue = new  ReferenceQueue();  
SoftReference  aSoftRef = new  SoftReference(aMyObject, queue);  

当这个SoftReference所软引用的Object被垃圾收集器回收的同时,aSoftRef所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。

在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。常用的方式为:

SoftReference ref = null;  
while ((ref = (EmployeeRef) q.poll()) != null) {  
    // 清除ref  
}  

弱引用(WeakReference)

弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:

public class Test {
    public static void main(String[] args) {
        WeakReference<String>reference=new WeakReference<String>(new String("zhangsan"));
        System.out.println(reference.get());
        System.gc();//通知JVM回收资源
        System.out.println(reference.get());
    }

}

结果:

zhangsan

null

只要JVM进行垃圾回收,被弱引用关联的对象必定会被回收掉。不过要注意的是,这里所说的被弱引用关联的对象是指只有弱引用与之关联,如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象(软引用也是如此)

弱引用可以和一个引用队列(ReferenceQueue)联合使用,(与软引用一样)如果弱引用所引用的对象被JVM回收,这个弱引用就会被加入到与之关联的引用队列中。

虚引用(PhantomReference)

虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。
虚引用一般用来检测对象是否被回收

  要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
  

public class Test {
    public static void main(String[] args) {
        ReferenceQueue<String> queue = new ReferenceQueue<String>();
        PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
        System.out.println(pr.get());
    }

}

利用软引用弱引用解决OOM问题

为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。

在应用中中可能需要使用到大量的图片,大量的图片可能会导致OOM异常,这时可以使用软引用来避免这个问题。

首先定义一个HashMap,保存软引用对象。

private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();

再来定义一个方法,保存Bitmap的软引用到HashMap。

 public void addBitmapToCache(String path) {
        // 强引用的Bitmap对象
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        // 软引用的Bitmap对象
        SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);
        // 添加该对象到Map中使其缓存
        imageCache.put(path, softBitmap);
    }

获取的时候,可以通过SoftReference的get()方法得到Bitmap对象。

 public Bitmap getBitmapByPath(String path) {
        // 从缓存中取软引用的Bitmap对象
        SoftReference<Bitmap> softBitmap = imageCache.get(path);
        // 判断是否存在软引用
        if (softBitmap == null) {
            return null;
        }
        // 取出Bitmap对象,如果由于内存不足Bitmap被回收,将取得空
        Bitmap bitmap = softBitmap.get();
        return bitmap;
    }

如果只是想避免OutOfMemory异常的发生,则可以使用软引用。如果对于应用的性能更在意,想尽快回收一些占用内存比较大的对象,则可以使用弱引用。
还有就是可以根据对象是否经常使用来判断。如果该对象可能会经常使用的,就尽量用软引用。如果该对象不被使用的可能性更大些,就可以用弱引用。
另外,和弱引用功能类似的是WeakHashMap。WeakHashMap对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的回收,回收以后,其条目从映射中有效地移除。WeakHashMap使用ReferenceQueue实现的这种机制。

refer:
Android开发优化之——使用软引用和弱引用
http://blog.csdn.net/arui319/article/details/8489451

猜你喜欢

转载自blog.csdn.net/wyg1230/article/details/78462814
今日推荐