面試官:ThreadLocal 為什么會(huì)內(nèi)存泄漏嗎?是怎么產(chǎn)生的?面試必問!
推薦閱讀
1、【地址已更新】8月26日最新免費(fèi)ChatGPT,附視頻號(hào)下載方法 2、 【私活必備】35款 SpringBoot/SpringCloud 開源項(xiàng)目,用來(lái)接私活掙錢真爽! 3、 大廠程序員提倡“防御性編程”:故意把代碼寫得很爛,萬(wàn)一自己被裁,要確保留下的代碼不可維護(hù)!
來(lái)源:blog.csdn.net/qunqunstyle99/article/details/9471725
- ThreadLocal是什么
- ThreadLocalMap
- 如何避免泄漏
ThreadLocal是什么
ThreadLocal是一個(gè)本地線程副本變量工具類。主要用于將私有線程和該線程存放的副本對(duì)象做一個(gè)映射,各個(gè)線程之間的變量互不干擾,在高并發(fā)場(chǎng)景下,可以實(shí)現(xiàn)無(wú)狀態(tài)的調(diào)用,特別適用于各個(gè)線程依賴不通的變量值完成操作的場(chǎng)景。
下圖為ThreadLocal的內(nèi)部結(jié)構(gòu)圖

從上面的結(jié)構(gòu)圖,我們已經(jīng)窺見ThreadLocal的核心機(jī)制:
- 每個(gè)Thread線程內(nèi)部都有一個(gè)Map。
- Map里面存儲(chǔ)線程本地對(duì)象(key)和線程的變量副本(value)
- 但是,Thread內(nèi)部的Map是由ThreadLocal維護(hù)的,由ThreadLocal負(fù)責(zé)向map獲取和設(shè)置線程的變量值。
所以對(duì)于不同的線程,每次獲取副本值時(shí),別的線程并不能獲取到當(dāng)前線程的副本值,形成了副本的隔離,互不干擾。
ThreadLocalMap

ThreadLocalMap是ThreadLocal的內(nèi)部類,沒有實(shí)現(xiàn)Map接口,用獨(dú)立的方式實(shí)現(xiàn)了Map的功能,其內(nèi)部的Entry也獨(dú)立實(shí)現(xiàn)。
和HashMap的最大的不同在于,ThreadLocalMap結(jié)構(gòu)非常簡(jiǎn)單,沒有next引用,也就是說(shuō)ThreadLocalMap中解決Hash沖突的方式并非鏈表的方式,而是采用線性探測(cè)的方式。(ThreadLocalMap如何解決沖突? )
在ThreadLocalMap中,也是用Entry來(lái)保存K-V結(jié)構(gòu)數(shù)據(jù)的。但是Entry中key只能是ThreadLocal對(duì)象,這點(diǎn)被Entry的構(gòu)造方法已經(jīng)限定死了。
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
注意了!!
Entry繼承自WeakReference(弱引用,生命周期只能存活到下次GC前),但只有Key是弱引用類型的,Value并非弱引用。(問題馬上就來(lái)了)
由于ThreadLocalMap的key是弱引用,而Value是強(qiáng)引用。這就導(dǎo)致了一個(gè)問題,ThreadLocal在沒有外部對(duì)象強(qiáng)引用時(shí),發(fā)生GC時(shí)弱引用Key會(huì)被回收,而Value不會(huì)回收。
當(dāng)線程沒有結(jié)束,但是ThreadLocal已經(jīng)被回收,則可能導(dǎo)致線程中存在ThreadLocalMap<null, Object>的鍵值對(duì),造成內(nèi)存泄露。(ThreadLocal被回收,ThreadLocal關(guān)聯(lián)的線程共享變量還存在)。
如何避免泄漏
為了防止此類情況的出現(xiàn),我們有兩種手段。
1、使用完線程共享變量后,顯示調(diào)用ThreadLocalMap.remove方法清除線程共享變量;
既然Key是弱引用,那么我們要做的事,就是在調(diào)用ThreadLocal的get()、set()方法時(shí)完成后再調(diào)用remove方法,將Entry節(jié)點(diǎn)和Map的引用關(guān)系移除,這樣整個(gè)Entry對(duì)象在GC Roots分析后就變成不可達(dá)了,下次GC的時(shí)候就可以被回收。
2、JDK建議ThreadLocal定義為private static,這樣ThreadLocal的弱引用問題則不存在了。
文章參考:
- www.jianshu.com/p/98b68c97df9b
- www.cnblogs.com/coshaho/p/5127135.html
---END---
最新 整理:??CSDN會(huì)員免費(fèi)電子書1000本、??黑客技術(shù)學(xué)習(xí)書籍大合集、??計(jì)算機(jī)技術(shù)專題書籍【69GB】、60多門編程語(yǔ)言學(xué)習(xí)書籍超級(jí)大合集(700+本PDF)、??計(jì)算機(jī)二級(jí)專題【183GB】等編程合集!

資源,怎么領(lǐng)取?
掃二維碼,加我微信,備注:編程合集
一定要備注:編程合集,不要急哦,工作忙完后就會(huì)通過!
