1. 程式人生 > 其它 >ThreadLocal真的會造成記憶體洩露?

ThreadLocal真的會造成記憶體洩露?

1、ThreadLocal知識體系

本文還是不能免俗,在回答這個問題之前需要先和大家介紹一下ThreadLocal的知識,使大家對ThreadLocal有一個相對全面的認識。

ThreadLocal本地執行緒變數,主要用於解決資料訪問的競爭,通常用於多租戶、全鏈路壓測、鏈路跟蹤中儲存執行緒上下文環境,在一個請求流轉中非常方便的獲取一些關鍵資訊,例如當前的租戶資訊、壓測標記。

ThreadLocal正如其名,本地執行緒變數,即資料儲存線上程自己的區域性變數中。

其整體架構如下圖所示:

ThreadLocal的核心設計理念總結如下:

  • 每一個執行緒物件會維護一個私有屬性:ThreadLocal.ThreadLocalMap threadLocals。

  • ThreadLocalMap內部結構為Key-Value鍵值對,其Key為ThreadLocal物件,Value為呼叫ThreadLocal的set方法設定的值。

一言以蔽之:ThreadLocal是將執行緒需要訪問的資料儲存線上程物件自身中,從而避免多執行緒的競爭

2、為什麼會被設計為弱引用呢?

接下來我們來看一下ThreadLocalMap的宣告:


什麼?Map中的用於儲存鍵值對的Entry為什麼要繼承WeakReference?

思考這個問題之前先和大家普及一下Java的4種引用型別,主要是在垃圾回收時java虛擬機器會根據不同的引用型別採取不同的措施。

  • 強引用:java預設的引用型別,例如 Object a = new Object();其中 a 為強引用,new Object()為一個具體的物件。一個物件從根路徑能找到強引用指向它,jvm虛擬機器就不會回收。

  • 軟引用(SoftReference):進行年輕代的垃圾回收不會觸發SoftReference所指向物件的回收;但如果觸發Full GC,那SoftReference所指向的物件將被回收。備註:是除了軟引用之外沒有其他強引用引用的情況下才會回收

  • 弱引用(WeakReference) :如果物件除了有弱引用指向它後沒有其他強引用關聯它,當進行年輕代垃圾回收時,該引用指向的物件就會被垃圾回收器回收。

  • 虛引用(PhantomeReference) 該引用指向的物件,無法對垃圾收集器收集物件時產生任何影響,但在執行垃圾回收後垃圾收集器會通過註冊在PhantomeReference上的佇列來通知應用程式物件被回收。

從四種弱引用的實際作用來說,主要是與垃圾回收器配合,決策什麼時候可以將被引用的物件回收。

理論看起來有點晦澀難懂,接下來筆者將以圖解的方式,爭取將該問題闡述清楚。


根據第一部分,聲明瞭一個TheadLocal物件,並且一個執行緒通過呼叫threadLocal物件的set(Object value)儲存了一個物件,其引用如上圖所示。

ThreadLocal的設計比較晦澀難懂,究其原因是我們通過threadLocal物件的set方法進行儲存值,但資料並不是儲存在ThreadLocal物件中,而是儲存在當前呼叫該方法的執行緒物件中。但從應用者的角度來看,我們操作的物件是ThreadLocal,從設計上來說就應該為它考慮。

試問一個問題:如果應用程式覺得ThreadLocal物件的使命完成,將threadLocal ref 設定為null,如果Entry中引用ThreadLocald物件的引用型別設定為強引用的話,會發生什麼問題?

答案是:ThreadLocal物件會無法被垃圾回收器回收,因為從thread物件出發,有強引用指向threadlocal obj。此時會違背使用者的初衷,造成所謂的記憶體洩露。

由於ThreadLocalMap中的key是指向ThreadLocal,故從設計角度來看,設計為弱引用,將不會干擾使用者的釋放ThreadLocal意圖。