1. 程式人生 > >詳解ThreadLocal為何存在記憶體洩漏

詳解ThreadLocal為何存在記憶體洩漏

ThreadLocal是Java中用於保證執行緒安全的一種措施,通過給每個執行緒分配一個專屬的值儲存空間,保證執行緒各自維護自己的變數,從而不會發生併發訪問問題。

但是ThreadLocal是存在著記憶體洩漏風險的,如果使用不當,容易發生memory leak錯誤。

首先解釋什麼是記憶體洩漏。

記憶體洩漏memory leak :是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩漏似乎不會有大的影響,但記憶體洩漏堆積後的後果就是記憶體溢位。

執行緒Thread物件中,每個執行緒物件內部都有一個的ThreadLocalMap物件。如果這個物件儲存了多個大物件,則可能早出記憶體溢位OOM。

為了防止這種情況發生,在ThreadLocal的原始碼中,有對應的策略,即呼叫 get()、set()、remove() 方法,均會清除 ThreadLocal內部的 記憶體。

ThreadLocal的內部是ThreadLocalMap。ThreadLocalMap內部是由一個Entry陣列組成。Entry類的建構函式為 Entry(弱引用的ThreadLocal物件, Object value物件)。因為Entry的key是一個弱引用的ThreadLocal物件,所以在 垃圾回收 之前,將會清除此Entry物件的key。那麼, ThreadLocalMap 中就會出現 key 為 null 的 Entry,就沒有辦法訪問這些 key 為 null 的 Entry 的 value。這些 value 被Entry物件引用,所以value所佔記憶體不會被釋放。若在指定的執行緒任務裡面,呼叫ThreadLocal物件的get()、set()、remove()方法,可以避免出現記憶體洩露。

ThreadLocal物件被GC回收了,那麼key變成了null。Map又是通過key拿到的value的物件。所以,GC在回收了key所佔記憶體後,沒法訪問到value的值,因為需要通過key才能訪問到value物件。另外,如圖所示的引用鏈:CurrentThread – Map – Entry – value ,所以,在當前執行緒沒有被回收的情況下,value所佔記憶體也不會被回收。所以可能會造成了記憶體溢位。

弱引用只要繼承WeakReference類即可。所以說,當ThreadLocal物件被GC回收了以後,Entry物件的key就變成null了。這個時候沒法訪問到 Object Value了。並且最致命的是,Entry持有Object value。所以,value的記憶體將不會被釋放。

因為上述的原因,在ThreadLocal這個類的get()、set()、remove()方法,均有實現回收 key 為 null 的 Entry 的 value所佔的記憶體。所以,為了防止記憶體洩露(沒法訪問到的記憶體),在不會再用ThreadLocal的執行緒任務末尾,呼叫一次 上述三個方法的其中一個即可。

因此,可以理解到為什麼JDK原始碼中要把Entry物件,用 弱引用的ThreadLocal物件,設計為key,那是因為要手動編寫程式碼釋放ThreadLocalMap中 key為null的Entry物件。

那麼GC什麼時候回收弱引用的物件?弱引用物件是存活到下一次垃圾回收發生之前物件。

綜上:JVM會自動回收某些物件將其置為null,從而避免OutOfMemory的錯誤。弱引用的物件可以被JVM設定為null。我們的程式碼通過判斷key是否為null,從而 手動釋放 記憶體洩露的記憶體。

為什麼要將ThreadLocal設計為弱引用?

答:因為弱引用的物件的生命週期直到下一次垃圾回收之前被回收。弱引用的物件將會被置為null。我們可以通過判斷弱引用物件是否已經為null,來進行相關的操作。在ThreadLocalMap中,如果鍵ThreadLocal已經被回收,說明ThreadLocal物件已經為null,所以其對應的值已經無法被訪問到。這個時候,需要及時手動編寫程式碼清理掉這個鍵值對,防止記憶體洩露導致的記憶體溢位。

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
}

強引用: 不會被回收的記憶體。

軟引用: 內部不足的時候回收的記憶體。

弱引用: 存活到垃圾回收前的記憶體。