jdk从1.2开始,把对象的引用分为四个类型,这四种引用类型从生命周期从长到短依次是:强引用、软引用、弱引用、虚引用。

Java引入这四种引用类型的目标主要有两个:
一个是可以让程序员通过代码的方式控制对象的生命周期;
一个是有利于jvm进行垃圾回收。

强引用

程序中最普遍的默认对象引用,也是程序开发中最常用的引用类型。JVM的垃圾回收期在任何时候都不会回收强引用。即使当内存空间不足抛出内存溢出(OOM),也不会回收具有强引用的对象来释放内存。我们可以显式的赋值对象为null,来告诉gc该对象不存在引用,这时gc就可以回收这个对象,但具体什么时候收集还是要取决于gc的算法。

比如ArraryList类的clear方法中就是通过将引用赋值为null来实现清理工作

/**
 * Removes all of the elements from this list.  The list will
 * be empty after this call returns.
 */
public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

软引用

软引用用来描述一些有用但不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。

比如下面的代码,即使obj赋值为null,软引用关联对象在内存充足的情况不会被回收。

public class TestSoftReference {
    public static void main(String[] args) {
        Obj obj = new Obj("SoftReference");
        SoftReference<Obj> sr = new SoftReference<>(obj);
        obj = null;
        System.gc();
        System.out.println(sr.get().getName());
    }
}

@Data
@AllArgsConstructor
class Obj {
    private String name;
}

运行结果:

SoftReference

弱引用

弱引用用来描述一些非必须的对象。弱引用相比于软引用拥有更短的生命周期,当JVM运行垃圾回收的时候,不论内存是否充足,都会回收只被弱引用对象关联的对象。在java中,用java.lang.ref.WeakReference类来表示。

比如下面的代码,obj赋值为null后,弱引用关联的对象会在执行垃圾回收后被回收。

public class TestWeakReference {
    public static void main(String[] args) {
        Obj obj = new Obj("WeakReference");
        WeakReference<Obj> wr = new WeakReference<>(obj);
        System.out.println(wr.get().getName());
        System.out.println("----------------");

        obj = null;
        System.out.println(wr.get().getName());
        System.out.println("----------------");

        // 推荐gc来回收
        System.gc();
        System.out.println(wr.get().getName());
    }
}

运行结果:

WeakReference
----------------
WeakReference
----------------
Exception in thread "main" java.lang.NullPointerException
    at com.xiao.temporary.TestRefrence.main(TestRefrence.java:25)

虚引用

顾名思义,就是形同虚设,即对对象而言是无感知的。与其他几种引用类型不同,虚引用不会影响和决定对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。

虚引用主要用来跟踪对象被垃圾回收的活动(即对象是否存活的监控)。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

比如下面的代码,通过是否入列ReferenceQueue可以判断对象是否被回收。

public class TestPhantomReference {
    public static void main(String[] args) {
        Obj obj = new Obj("PhantomReference");
        ReferenceQueue<Obj> referenceQueue = new ReferenceQueue<>();
        PhantomReference<Obj> pr = new PhantomReference<>(obj, referenceQueue);
        // 永远为null
        System.out.println("pr.get:" + pr.get());
        // 是否入队,和是否被回收有关
        System.out.println("pr.isEnqueued:" + pr.isEnqueued());
        // poll一下referenceQueue中的对象看看
        System.out.println("referenceQueue.poll:" + referenceQueue.poll());
        System.out.println("----------------");

        obj = null;
        System.out.println("pr.get:" + pr.get());
        System.out.println("pr.isEnqueued:" + pr.isEnqueued());
        System.out.println("referenceQueue.poll:" + referenceQueue.poll());
        System.out.println("----------------");

        // 推荐gc来回收
        System.gc();
        System.out.println("pr.get:" + pr.get());
        System.out.println("pr.isEnqueued:" + pr.isEnqueued());
        System.out.println("referenceQueue.poll:" + referenceQueue.poll());
    }
}

运行结果:

pr.get:null
pr.isEnqueued:false
referenceQueue.poll:null
----------------
pr.get:null
pr.isEnqueued:false
referenceQueue.poll:null
----------------
pr.get:null
pr.isEnqueued:true
referenceQueue.poll:java.lang.ref.PhantomReference@610455d6

总结

引用类型被垃圾回收时间用途生存时间
强引用从来不会对象的一般状态JVM停止运行时
软引用内存不足时对象缓存内存不足时
弱引用jvm垃圾回收时对象缓存gc运行后
虚引用未知未知未知
最后编辑:2020年06月28日 ©著作权归作者所有

发表评论