SoftReference、WeakReference、PhantomRefrence分析和比较

级别

什么时候被垃圾回收

用途

生存时间

强引用

从来不会

对象的一般状态

JVM停止运行时终止

软引用

在内存不足时

优化内存使用

内存不足时终止

弱引用

在垃圾回收时

对象缓存

gc运行后终止

虚引用

 

软引用、弱引用测试

/**
 * cd /Users/gl/IntelliJProjects/JBase/jdk/build/classes/main
 * java -Xms20M -Xmx20M -Xmn5M reference.TestReference
 */
public class TestReference {

    private static int K = 1024;

    private static int M = 1024 * K;

    public static void main(String[] args) {
        testSoftReference();
        testWeakReference();
    }

    /**
     java.lang.ref.SoftReference@610455d6
     null
     java.lang.ref.SoftReference@610455d6

     代码`byte[] c = new byte[5 * M];` 会导致内存溢出, 但是在溢出前会进行GC,
     所以`softReference.get()`会返回`null`(被软引用对象引用的`b`被回收了), 并且应用对象`softReference`会被加入引用队列.

     */
    private static void testSoftReference(){
        byte[] a = new byte[10 * M];
        byte[] b = new byte[2 * M];
        ReferenceQueue queue = new ReferenceQueue();
        SoftReference softReference = new SoftReference(b, queue);
        System.out.println(softReference);
        b = null;
        try {
            byte[] c = new byte[5 * M];
        }catch (Error error){
            System.out.println(softReference.get());
            //SoftReference被加入引用队列
            System.out.println(queue.poll());
        }
    }


    /**
     [B@4e25154f
     null

     *在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
     * 不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
     */
    private static void testWeakReference(){
        byte[] a = new byte[10 * M];
        ReferenceQueue queue = new ReferenceQueue();
        WeakReference weakReference = new WeakReference(a, queue);
        a = null;
        System.out.println(weakReference.get());
        System.gc();
        System.out.println(weakReference.get());
    }
}

使用SoftReference优化内存 

    //创建Image对象 
  Image image = new Image();
  … 
  //使用 image 
  … 
  //使用完了image,将它设置为soft 引用类型,并且释放强引用 
  SoftReference sr = new SoftReference(image); 
  image = null; 
   … 
   //下次使用时 
   if (sr != null) {
image = sr.get();    }else{    //低内存,GC,释放image,因此需要重新装载    image = new Image();    sr = new SoftReference(image);   }

理解

 虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,

在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。

为了确保可回收对象保持原样,无法检索虚引用的值:虚幻引用的get方法始终返回null。

Phantom reference objects, which are enqueued after the collector determines that their referents may otherwise be reclaimed. Phantom references are

most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.

If the garbage collector determines at a certain point in time that the referent of a phantom reference is phantom reachable, then at that time or at some

later time it will enqueue the reference.

In order to ensure that a reclaimable object remains so, the referent of a phantom reference may not be retrieved: The get method of a phantom

reference always returns null.

Unlike soft and weak references, phantom references are not automatically cleared by the garbage collector as they are enqueued. An object that is

reachable via phantom references will remain so until all such references are cleared or themselves become unreachable.

PhantomReference 有两个好处:

它可以让我们准确地知道对象何时被从内存中删除

这个特性可以被用于一些特殊的需求中(例如 Distributed GC, XWork 和 google-guice 中也使用 PhantomReference 做了一些清理性工作).

它可以避免 finalization 带来的一些根本性问题。

上文提到 PhantomReference 的唯一作用就是跟踪 referent 何时GC, 但是 WeakReference 也有对应的功能, 两者的区别到底在哪呢 ?

这就要说到 Object 的 finalize 方法, 此方法将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用,

这 GC 无法回收这个对象并有可能引起任意次 GC, 最后的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference

就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用, 也就不会出现上述问题,

当然这是一个很极端的例子, 一般不会出现.

    /**
     *
     * 相当于
     * Object o = new Object();
     * o =  null;
     */
    private static void testPhantomReference(){
        ReferenceQueue queue = new ReferenceQueue();
        PhantomReference ref = new PhantomReference(new Object(), queue);
        System.out.println(ref.get());

    }

参考:

Java Reference 

原文地址:https://www.cnblogs.com/yuyutianxia/p/3548555.html