Lucene原始碼解析--Lucene中的CloseableThreadLocal類

百聯達發表於2013-06-12
Java的ThreadLocal類有一個嚴重的瑕疵那就是即便在ThreadLocal 例項本身不在被引用的情況下,其中儲存的東西也需要相當長的時間才能被解除引用。原因是所有的ThreadLocal 例項共享一個map,而該map只會在指定的時間段內清除過期的條目。

CloseableThreadLocal透過接收WeakReference型別的值並且保持對每個儲存值一個硬應用,當呼叫close()方法的時候會清楚所有的引用,這樣GC就可以回收所佔用的記憶體空間。

Java 中一共有 4 種型別的引用 : StrongReference、 SoftReference、 WeakReference 以及 PhantomReference。

一:Strong Reference
StrongReference 是 Java 的預設引用實現,  它會盡可能長時間的存活於 JVM 內, 當沒有任何物件指向它時 GC 執行後將會被回收
    public static void main(String[] args) {
        Object bject = new Object();

        // 透過賦值建立 StrongReference
        Object referent = object;

        System.out.println(referent == object);
        /************** true **********************/

        bject = null;
        System.gc();

        // StrongReference 在 GC 後不會被回收

        System.out.println(null == referent);
        /************** false **********************/
    }

二:WeakReference & WeakHashMap
WeakReference, 顧名思義,  是一個弱引用,  當所引用的物件在 JVM 內不再有強引用時, GC 後 weak reference 將會被自動回收
    public static void main(String[] args) {
        Object bject = new Object();

        // 建立 WeakReference
        WeakReference referent = new WeakReference(object);

        bject = null;
        System.gc();

        //一旦沒有指向 referent 的強引用, WeakReference 在 GC 後會被自動回收

        System.out.println(null == referent.get());
        /************** false **********************/
    }

WeakHashMap 使用 WeakReference 作為 key, 一旦沒有指向 key 的強引用, WeakHashMap 在 GC 後將自動刪除相關的 entry

public static void main(String[] args) throws Exception {
        Map weakHashMap = new WeakHashMap();
        Object key = new Object();
        Object value = new Object();
        weakHashMap.put(key, value);

        System.out.println(weakHashMap.containsValue(value));

        key = null;
        System.gc();

        /**
         * 等待無效 entries 進入 ReferenceQueue 以便下一次呼叫 getTable 時被清理
         */
        Thread.sleep(1000);

        /**
         * 一旦沒有指向 key 的強引用, WeakHashMap 在 GC 後將自動刪除相關的 entry
         */
        System.out.println(weakHashMap.containsValue(value));
    }

三:SoftReference
SoftReference 於 WeakReference 的特性基本一致, 最大的區別在於 SoftReference 會盡可能長的保留引用直到 JVM 記憶體不足時才會被回收(虛擬機器保證), 這一特性使得 SoftReference 非常適合快取應用

四:PhantomReference
Phantom Reference(幽靈引用) 與 WeakReference 和 SoftReference 有很大的不同,  因為它的 get() 方法永遠返回 null, 這也正是它名字的由來。PhantomReference 唯一的用處就是跟蹤 referent  何時被 enqueue 到 ReferenceQueue 中.

五:RererenceQueue
當一個 WeakReference 開始返回 null 時, 它所指向的物件已經準備被回收, 這時可以做一些合適的清理工作.   將一個 ReferenceQueue 傳給一個 Reference 的建構函式, 當物件被回收時, 虛擬機器會自動將這個物件插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 來清除 key 已經沒有強引用的 entries.
    public static void main(String[] args) throws Exception {
        Object bject = new Object();
        ReferenceQueue referenceQueue = new ReferenceQueue();
        WeakReference weakReference = new WeakReference(object,
                referenceQueue);

        System.out.println(weakReference.isEnqueued());
        /************** false **************/
        Reference extends Object> polled = referenceQueue.poll();
        System.out.println(null == polled);
        /************** true **************/
        // 取消引用
        bject = null;
        System.gc();

        System.out.println(weakReference.isEnqueued());
        /************** true **************/
        Reference extends Object> removed = referenceQueue.remove();
        System.out.println(null == removed);
        /************** false **************/
    }



Object 的 finalize 方法, 將在 gc 執行前被呼叫, 如果某個物件過載了 finalize 方法並故意在方法內建立本身的強引用,  這將導致這一輪的 GC 無法回收這個物件並有可能引 起任意次 GC, 最後的結果就是明明 JVM 內有很多 Garbage 卻 OutOfMemory, 使用 PhantomReference 就可以避免這個問題, 因為 PhantomReference 是在 finalize 方法執行後回收的,也就意味著此時已經不可能拿到原來的引用,  也就不會出現上述問題。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28624388/viewspace-763745/,如需轉載,請註明出處,否則將追究法律責任。

相關文章