ThreadLocal 內存泄漏問題深入分析


寫在前面

ThreadLocal 基本用法本文就不介紹了,如果有不知道的小伙伴可以先了解一下,本文只研究 ThreadLocal 內存泄漏這一問題。

ThreadLocal 會發生內存泄漏嗎?

先給出結論:如果你使用不當是有可能發生內存泄露的

ThreadLocal 和 當前 Thread 棧堆布局圖

每個 Thread 里面都有一個 ThreadLocalMap,而 ThreadLocalMap 中真正承載數據的是一個 Entry 數組,Entry 的 Key 是 threadlocal 對象的弱引用。

這里或許有的小伙伴有疑問,Entry 的 Key 是 threadlocal 對象的弱引用,為什么要用弱引用,換成強引用行不行?

首先,我們先了解一下什么是弱引用?

弱引用一般是用來描述非必需對象的,被弱引用關聯的對象只能生存到下一次垃圾收集發生之前。當垃圾收集器工作時,無論當前內存是否足夠,都會回收掉只被弱引用關聯的對象

實際開發中,當我們不需要 threadlocal 后,為了 GC 將 threadlocal 變量置為 null,沒有任何強引用指向堆中的 threadlocal 對象時,堆中的 threadlocal 對象將會被 GC 回收,假設現在 Key 持有的是 threadlocal 對象的強引用,如果當前線程仍然在運行,那么從當前線程一直到 threadlocal 對象還是存在強引用,由於當前線程仍在運行的原因導致 threadlocal 對象無法被 GC,這就發生了內存泄漏。相反,弱引用就不存在此問題,當棧中的 threadlocal 變量置為 null 后,堆中的 threadlocal 對象只有一個 Key 的弱引用關聯,下一次 GC 的時候堆中的 threadlocal 對象就會被回收,使用弱引用對於 threadlocal 對象而言是不會發生內存泄漏的

那么,第二個問題來了,是不是 Key 持有的是 threadlocal 對象的弱引用就一定不會發生內存泄漏呢?

結論是:如果你使用不當還是有可能發生內存泄露,但是,這里發生內存泄漏的地方和上面不同。

當 threadlocal 使用完后,將棧中的 threadlocal 變量置為 null,threadlocal 對象下一次 GC 會被回收,那么 Entry 中的與之關聯的弱引用 key 就會變成 null,如果此時當前線程還在運行,那么 Entry 中的 key 為 null 的 Value 對象並不會被回收(存在強引用),這就發生了內存泄漏,當然這種內存泄漏分情況,如果當前線程執行完畢會被回收,那么 Value 自然也會被回收,但是如果使用的是線程池呢,線程跑完任務以后放回線程池(線程沒有銷毀,不會被回收),Value 會一直存在,這就發生了內存泄漏。

如何更好的降低內存泄漏的風險呢?

ThreadLocal 為了降低內存泄露的可能性,在 set,get,remove 的時候都會清除此線程 ThreadLocalMap 里 Entry 數組中所有 Key 為 null 的 Value。所以,當前線程使用完 threadlocal 后,我們可以通過調用 ThreadLocal 的 remove 方法進行清除從而降低內存泄漏的風險。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM