ThreadLocal 內存泄漏

一、ThreadLocal實現原理

ThreadLocal的實現是這樣的:每一個Thread 維護一個 ThreadLocalMap 映射表,這個映射表的 key 是 ThreadLocal實例自己,value 是真正須要存儲的 Objectapi

也就是說 ThreadLocal 自己並不存儲值,它只是做爲一個 key 來讓線程從 ThreadLocalMap 獲取 value。值得注意的是圖中的虛線,表示 ThreadLocalMap 是使用 ThreadLocal 的弱引用做爲 Key 的,弱引用的對象在 GC 時會被回收。app

二、內存泄漏緣由

若是外界沒有強引用指向當前ThreadLocal,當系統GC的時候就會把該對象清空,形成存在key爲null的Entry,這樣Value就永遠沒法訪問到了,而且若是當前線程一直不結束,就會形成內存泄漏。url

由於存在一條強引用:threadRef→ thread→ threadLocalMap→ entry→ value。spa

 

其實,ThreadLocalMap的設計中已經考慮到這種狀況,也加上了一些防禦措施:在ThreadLocalget(),set(),remove()的時候都會清除線程ThreadLocalMap裏全部keynullvalue線程

可是這些被動的預防措施並不能保證不會內存泄漏:設計

  • 使用staticThreadLocal,延長了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。生命週期

下面咱們分兩種狀況討論:內存

  • key 使用強引用:引用的ThreadLocal的對象被回收了,可是ThreadLocalMap還持有ThreadLocal的強引用,若是沒有手動刪除,ThreadLocal不會被回收,致使Entry內存泄漏。
  • key 使用弱引用:引用的ThreadLocal的對象被回收了,因爲ThreadLocalMap持有ThreadLocal的弱引用,即便沒有手動刪除,ThreadLocal也會被回收。value在下一次ThreadLocalMap調用set,getremove的時候會被清除。

比較兩種狀況,咱們能夠發現:因爲ThreadLocalMap的生命週期跟Thread同樣長,若是都沒有手動刪除對應key,都會致使內存泄漏,可是使用弱引用能夠多一層保障:弱引用ThreadLocal不會內存泄漏,對應的value在下一次ThreadLocalMap調用set,get,remove的時候會被清除

所以,ThreadLocal內存泄漏的根源是:因爲ThreadLocalMap的生命週期跟Thread同樣長,若是沒有手動刪除對應key就會致使內存泄漏,而不是由於弱引用。

ThreadLocal 最佳實踐

綜合上面的分析,咱們能夠理解ThreadLocal內存泄漏的來龍去脈,那麼怎麼避免內存泄漏呢?

  • 每次使用完ThreadLocal,都調用它的remove()方法,清除數據。

在使用線程池的狀況下,沒有及時清理ThreadLocal,不只是內存泄漏的問題,更嚴重的是可能致使業務邏輯出現問題。因此,使用ThreadLocal就跟加鎖完要解鎖同樣,用完就清理。

相關文章
相關標籤/搜索