理解 Java 的 GC 與 引用

Java中一共有4種類型的引用:StrongReference、SoftReference、WeakReference以及PhantomReference;這4種類型的引用與GC有着密切的關係,讓咱們逐一來看它們的定義和使用場景:

1.StrongReference

StrongReference是Java的默認引用實現,它會盡量長時間的存活於JVM內,當沒有任何對象指向它時GC執行後將會被回收  html

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
相關文章
相關標籤/搜索