ThreadLocal尾篇:ThreadLocal記憶體洩漏到底因為啥
最近看ThreadLocal記憶體洩漏這塊,網上說法很多。呼聲最多的是因為弱引用導致的記憶體洩漏。
為啥記憶體洩漏
1.ThreadLocal什麼情況下被回收
ThreadLocal
在ThreadLocalMap
中是以一個弱引用身份被Entry中的Key引用,當GC發生時,ThreadLocal
會不會被回收?
這裡就用到引用的知識點了。 在java的四種引用講過。當一個物件被強引用指向時(這裡指可達)。垃圾回收器不會回收他。
例如
ThreadLocal threadlocal1 = new ThreadLocal();
threadlocal1.set("測試");
複製程式碼
ThreadLocal
- 強引用:
threadlocal1
對應圖中【1】 - 弱引用:
Key(WeakReference(threadlocal1))
對應圖中的【2】
所以GC發生時,堆內ThreadLocal物件不會被回收。
但是當我們把threadlocal1 =null;
斷開強引用時,此時ThreadLoca
l物件只有一個弱引用,那麼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
的get(),set(),reomve()
方法,都會清除掉執行緒ThreadLocalMap
中所有Entry中Key為null的Value,並將整個Entry設定為null,利於下次記憶體回收。
3.洩漏的真正原因
所以:ThreadLocal記憶體洩漏的真正原因:
-
ThreadLocalMap
的生命週期跟Thread一樣長,如果執行緒池不退,例如執行緒池,可能造成記憶體洩漏 - 使用完沒有及時清理,不再呼叫
get()、set()、remove()
對髒Entry進行清理
這才是造成記憶體洩漏的真正原因。
總結:
使用完及時remove,才是最正宗的使用方式。
如有錯誤,請告知,共同學習。
參考: