1. 程式人生 > 程式設計 >ThreadLocal尾篇:ThreadLocal記憶體洩漏到底因為啥

ThreadLocal尾篇:ThreadLocal記憶體洩漏到底因為啥

最近看ThreadLocal記憶體洩漏這塊,網上說法很多。呼聲最多的是因為弱引用導致的記憶體洩漏。

為啥記憶體洩漏

1.ThreadLocal什麼情況下被回收

ThreadLocalThreadLocalMap中是以一個弱引用身份被Entry中的Key引用,當GC發生時,ThreadLocal會不會被回收?

這裡就用到引用的知識點了。 在java的四種引用講過。當一個物件被強引用指向時(這裡指可達)。垃圾回收器不會回收他。

例如

ThreadLocal threadlocal1 = new ThreadLocal();
threadlocal1.set("測試");
複製程式碼

ThreadLocal

物件此時有兩種索引指向的。

  • 強引用:threadlocal1對應圖中【1】
  • 弱引用:Key(WeakReference(threadlocal1))對應圖中的【2】

所以GC發生時,堆內ThreadLocal物件不會被回收。

但是當我們把threadlocal1 =null;斷開強引用時,此時ThreadLocal物件只有一個弱引用,那麼GC發生時,ThreadLocal物件被回收了,Entry變成了一個key為null的Entry。也叫髒Entry

特點是:

  • key為null,value不能被應用程式訪問到,因為我們已經沒有引用到他的引用了
  • Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
    鏈存在,當前執行緒遲遲不結束(例如執行緒池),但不能被使用,成了髒資料,造成了記憶體洩漏。

看上去好像真是軟引用造成的記憶體洩漏。


2.為啥用弱引用

那換做強引用分析: ThreadLocal物件被兩個強引用指向

  • 強引用: threadlocal1
  • 強引用: Entry.key

當我們斷開程式中的強引用 threadlocal1時。ThreadLocal物件仍然被強引用Entry.key指向,不會回收,這就造成,ThreadLocal物件與 value都成為了髒資料。


對比這兩種情況:不管軟引用還是強引用,都可能出現記憶體洩漏問題,弱引用反而將記憶體洩漏的程度降低

利用弱引用的Entry會有key為null這個特徵

,可以識別哪些是不用的資料,進行清理操作,弱引用 反而提高了ThreadLocal的安全性。

事實上當呼叫ThreadLocalget(),set(),reomve()方法,都會清除掉執行緒ThreadLocalMap中所有Entry中Key為null的Value,並將整個Entry設定為null,利於下次記憶體回收。

3.洩漏的真正原因

所以:ThreadLocal記憶體洩漏的真正原因:

  1. ThreadLocalMap的生命週期跟Thread一樣長,如果執行緒池不退,例如執行緒池,可能造成記憶體洩漏
  2. 使用完沒有及時清理,不再呼叫get()、set()、remove()對髒Entry進行清理

這才是造成記憶體洩漏的真正原因。

總結:

使用完及時remove,才是最正宗的使用方式

如有錯誤,請告知,共同學習。

參考: