理解 Java 的 GC 与 引用

Java中一共有4种类型的引用:StrongReference、SoftReference、WeakReference以及PhantomReference;这4种类型的引用与GC有着密切的关系,让我们逐一来看它们的定义和使用场景:

1.StrongReference

StrongReference是Java的默认引用实现,它会尽可能长时间的存活于JVM内,当没有任何对象指向它时GC执行后将会被回收 

    public void strongReference() {
        Object referent = new Object();
        // 通过赋值创建 StrongReference
        Object strongReference = referent;
        assertSame(referent, strongReference);
        referent = null;
        System.gc();
        // StrongReference 在 GC 后不会被回收
        assertNotNull(strongReference);
    }

2.WeakReference&WeakHashMap

WeakReference,顾名思义,是一个弱引用,当所引用的对象在JVM内不再有强引用时,GC后weakreference将会被自动回收 
    public void weakReference() {
        Object referent = new Object();
        WeakReference<Object> weakRerference = new WeakReference<Object>(referent);
        assertSame(referent, weakRerference.get());
        referent = null;
        System.gc();
        // 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回
        assertNull(weakRerference.get());
    }
WeakHashMap使用WeakReference作为key,一旦没有指向key的强引用,WeakHashMap在GC后将自动删除相关的entry
    public void weakHashMap() throws InterruptedException {
        Map<Object, Object> weakHashMap = new WeakHashMap<Object, Object>();
        Object key = new Object();
        Object value = new Object();
        weakHashMap.put(key, value);
        assertTrue(weakHashMap.containsValue(value));
        key = null;
        System.gc();
        // 等待无效 entries 进入 ReferenceQueue 以便下一次调用 getTable 时被清理
        Thread.sleep(1000);
        // 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry
        assertFalse(weakHashMap.containsValue(value));
    }

3.SoftReference

SoftReference于WeakReference的特性基本一致,最大的区别在于SoftReference会尽可能长的保留引用直到JVM内存不足时才会被回收(虚拟机保证),这一特性使得SoftReference非常适合缓存应用 
    public void softReference() {
        Object referent = new Object();
        SoftReference<Object> softRerference = new SoftReference<Object>(referent);
        assertNotNull(softRerference.get());
        referent = null;
        System.gc();
        // soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用
        assertNotNull(softRerference.get());
    }

4.PhantomReference

PhantomReference(幽灵引用)与WeakReference和SoftReference有很大的不同,因为它的get()方法永远返回null,这也正是它名字的由来 
    public void phantomReferenceAlwaysNull() {
        Object referent = new Object();
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(referent, new ReferenceQueue<Object>());
        // phantom reference 的 get 方法永远返回 null
        assertNull(phantomReference.get());
    }
诸位可能要问,一个永远返回null的reference要来何用,请注意构造PhantomReference时的第二个参数ReferenceQueue(事实上WeakReference&SoftReference也可以有这个参数),PhantomReference唯一的用处就是跟踪referent何时被enqueue到ReferenceQueue中.

5.RererenceQueue

当一个WeakReference开始返回null时,它所指向的对象已经准备被回收,这时可以做一些合适的清理工作.将一个ReferenceQueue传给一个Reference的构造函数,当对象被回收时,虚拟机会自动将这个对象插入到ReferenceQueue中,WeakHashMap就是利用ReferenceQueue来清除key已经没有强引用的entries. 
    public void referenceQueue() throws InterruptedException {
        Object referent = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
        WeakReference<Object> weakReference = new WeakReference<Object>(referent, referenceQueue);
        assertFalse(weakReference.isEnqueued());
        Reference<? extends Object> polled = referenceQueue.poll();
        assertNull(polled);
        referent = null;
        System.gc();
        assertTrue(weakReference.isEnqueued());
        Reference<? extends Object> removed = referenceQueue.remove();
        assertNotNull(removed);
    }

6.PhantomReferencev&WeakReference

PhantomReference有两个好处,其一,它可以让我们准确地知道对象何时被从内存中删除,这个特性可以被用于一些特殊的需求中(例如DistributedGC,XWork和google-guice中也使用PhantomReference做了一些清理性工作).其二,它可以避免finalization带来的一些根本性问题,上文提到PhantomReference的唯一作用就是跟踪referent何时被enqueue到ReferenceQueue中,但是WeakReference也有对应的功能,两者的区别到底在哪呢?这就要说到Object的finalize方法,此方法将在gc执行前被调用,如果某个对象重载了finalize方法并故意在方法内创建本身的强引用,这将导致这一轮的GC无法回收这个对象并有可能引起任意次GC,最后的结果就是明明JVM内有很多Garbage却OutOfMemory,使用PhantomReference就可以避免这个问题,因为PhantomReference是在finalize方法执行后回收的,也就意味着此时已经不可能拿到原来的引用,也就不会出现上述问题,当然这是一个很极端的例子,一般不会出现.

7.对比

taken from http://mindprod.com/jgloss/phantom.html
Soft vs Weak vs Phantom References Type Purpose Use When GCed Implementing Class
Strong Reference
An ordinary reference. Keeps objects alive as long as they are referenced.
normal reference.
Any object not pointed to can be reclaimed.
default
Soft Reference
Keeps objects alive provided there’s enough memory.
to keep objects alive even after clients have removed their references (memory-sensitive caches), in case clients start asking for them again by key.
After a first gc pass, the JVM decides it still needs to reclaim more space.
java.lang.ref.SoftReference
Weak Reference
Keeps objects alive only while they’re in use (reachable) by clients.
Containers that automatically delete objects no longer in use.
After gc determines the object is only weakly reachable
java.lang.ref.WeakReference 
java.util.WeakHashMap
Phantom Reference Lets you clean up after finalization but before the space is reclaimed (replaces or augments the use offinalize()) Special clean up processing
After finalization.
java.lang.ref.PhantomReference

你可能感兴趣的:(理解 Java 的 GC 与 引用)