ThreadLocal
的實現是這樣的:每一個Thread
維護一個 ThreadLocalMap
映射表,這個映射表的 key
是 ThreadLocal
實例自己,value
是真正須要存儲的 Object
。api
也就是說 ThreadLocal
自己並不存儲值,它只是做爲一個 key
來讓線程從 ThreadLocalMap
獲取 value
。值得注意的是圖中的虛線,表示 ThreadLocalMap
是使用 ThreadLocal
的弱引用做爲 Key
的,弱引用的對象在 GC 時會被回收。app
若是外界沒有強引用指向當前ThreadLocal,當系統GC的時候就會把該對象清空,形成存在key爲null的Entry,這樣Value就永遠沒法訪問到了,而且若是當前線程一直不結束,就會形成內存泄漏。url
由於存在一條強引用:threadRef→ thread→ threadLocalMap→ entry→ value。spa
其實,ThreadLocalMap
的設計中已經考慮到這種狀況,也加上了一些防禦措施:在ThreadLocal
的get()
,set()
,remove()
的時候都會清除線程ThreadLocalMap
裏全部key
爲null
的value
。線程
可是這些被動的預防措施並不能保證不會內存泄漏:設計
static
的ThreadLocal
,延長了ThreadLocal
的生命週期,可能致使的內存泄漏。ThreadLocal
又再也不調用get()
,set()
,remove()
方法,那麼就會致使內存泄漏。從表面上看內存泄漏的根源在於使用了弱引用。網上的文章大多着重分析ThreadLocal
使用了弱引用會致使內存泄漏,可是另外一個問題也一樣值得思考:爲何使用弱引用而不是強引用?code
咱們先來看看官方文檔的說法:對象
To help deal with very large and long-lived usages, the hash table entries use WeakReferences for keys.
爲了應對很是大和長時間的用途,哈希表使用弱引用的 key。生命週期
下面咱們分兩種狀況討論:內存
ThreadLocal
的對象被回收了,可是ThreadLocalMap
還持有ThreadLocal
的強引用,若是沒有手動刪除,ThreadLocal
不會被回收,致使Entry
內存泄漏。ThreadLocal
的對象被回收了,因爲ThreadLocalMap
持有ThreadLocal
的弱引用,即便沒有手動刪除,ThreadLocal
也會被回收。value
在下一次ThreadLocalMap
調用set
,get
,remove
的時候會被清除。比較兩種狀況,咱們能夠發現:因爲ThreadLocalMap
的生命週期跟Thread
同樣長,若是都沒有手動刪除對應key
,都會致使內存泄漏,可是使用弱引用能夠多一層保障:弱引用ThreadLocal
不會內存泄漏,對應的value
在下一次ThreadLocalMap
調用set
,get
,remove
的時候會被清除。
所以,ThreadLocal
內存泄漏的根源是:因爲ThreadLocalMap
的生命週期跟Thread
同樣長,若是沒有手動刪除對應key
就會致使內存泄漏,而不是由於弱引用。
綜合上面的分析,咱們能夠理解ThreadLocal
內存泄漏的來龍去脈,那麼怎麼避免內存泄漏呢?
ThreadLocal
,都調用它的remove()
方法,清除數據。在使用線程池的狀況下,沒有及時清理ThreadLocal
,不只是內存泄漏的問題,更嚴重的是可能致使業務邏輯出現問題。因此,使用ThreadLocal
就跟加鎖完要解鎖同樣,用完就清理。