每個(gè)線程都有一個(gè)threadLocals對(duì)象 其實(shí)質(zhì)是一個(gè)鏈表
鏈表的每個(gè)元素就是一個(gè)entry企垦,這個(gè)entry的key是WeakReference<threadlocal>,value則是我們?cè)O(shè)置值
所以這就意味著 當(dāng)我們?cè)O(shè)置多個(gè)threadlocal對(duì)象則在thread的threadLocals中就有多個(gè)entry扳躬。
因?yàn)閑ntry的key是弱引用,其會(huì)在jvm發(fā)生gc(無論是minor 姚垃,major歇父,full gc)之后被回收。
內(nèi)存泄漏
1.比如我們的key 因?yàn)間c 被回收 但是value 則永遠(yuǎn)無法在訪問
解決方案:在ThreadLocal的get(),set(),remove()的時(shí)候都會(huì)清除線程ThreadLocalMap里所有key為null的value沙合。
2.就是我們的線程一直不結(jié)束奠伪,且也一直不使用上述三個(gè)方法的api,或者就算使用了get和set但是一直 沒使用remove 而在此期間key也沒有回收首懈,這就導(dǎo)致這個(gè)內(nèi)存一值被占用绊率。
Threadlocal
一個(gè)threadlocal對(duì)象為每一個(gè)線程創(chuàng)建一個(gè)類似map(其實(shí)是數(shù)組內(nèi)部存儲(chǔ)一個(gè)entry對(duì)象,entry的key是弱引用包裝的threadlocal value就是我們?cè)O(shè)置的值)
這個(gè)map會(huì)賦值給該線程 作為線程的一個(gè)屬性究履!因此這邊需要注意的是當(dāng)我們用多個(gè)threadlocal操作一個(gè)線程 則map里面包含的數(shù)據(jù)就是多個(gè)滤否!
弱引用對(duì)象會(huì)在threadlocal被jvm gc的時(shí)候被回收(沒有任何強(qiáng)引用指向threadlocal實(shí)例)放入到隊(duì)列中(如果我沒又設(shè)置隊(duì)列則不放入隊(duì)列)
內(nèi)存溢出或者內(nèi)存泄漏的原因
我們這樣一般很少會(huì)用很多threadlocal對(duì)象操作線程,所以一般都是一個(gè)threadlocal對(duì)應(yīng)多個(gè)線程
那么就相當(dāng)于每個(gè)線程都攜帶一個(gè)map 一個(gè)map包含這個(gè)弱引用的threadlocal的key和對(duì)應(yīng)value
一般內(nèi)存異常是指多個(gè)線程都沒有回收map中的結(jié)果進(jìn)而導(dǎo)致內(nèi)存溢出最仑,當(dāng)然也可以理解為內(nèi)存泄漏
因?yàn)樵谶@段時(shí)間我們不使用他們 也不刪除藐俺。這就導(dǎo)致內(nèi)存一直被占用但是無法釋放!
當(dāng)我們使用的threadlocal不是全局對(duì)象 是可以被回收的 那么我們的map中就有可能存在key為null情況這就導(dǎo)致我們無法使用我們value
這才是真正的內(nèi)存泄漏泥彤,而對(duì)于上述key不為null還
所以 如果我們定義一個(gè)全局變量threadlocal 那么對(duì)于單線程 就算我們?cè)O(shè)置了 不刪除 也不會(huì)有內(nèi)存泄漏欲芹,但是對(duì)于線程池 或者遲遲不結(jié)束的線程!
那么我們不刪除threadlocal 那么我們下次在使用會(huì)覆蓋的吟吝。
但是我們定義一個(gè)局部變量threadlocal 那么對(duì)于線程池 或者遲遲不結(jié)束的線程菱父! 則有可能存在key是null的內(nèi)存泄漏數(shù)據(jù)!
所以我們需要解決這個(gè)內(nèi)存泄漏
而對(duì)于set get remove都會(huì)刪除key=null的entry
但是需要注意的是 其必須遇到key=null時(shí)候才會(huì)刪除,如果我們get的時(shí)候就算有key=null的情況 但是沒遇上則是不會(huì)刪除的
key 使用強(qiáng)引用:引用的ThreadLocal的對(duì)象被回收了滞伟,但是ThreadLocalMap還持有ThreadLocal的強(qiáng)引用揭鳞,如果沒有手動(dòng)刪除,ThreadLocal不會(huì)被回收梆奈,導(dǎo)致Entry內(nèi)存泄漏野崇。
key 使用弱引用:引用的ThreadLocal的對(duì)象被回收了,由于ThreadLocalMap持有ThreadLocal的弱引用亩钟,即使沒有手動(dòng)刪除乓梨,ThreadLocal也會(huì)被回收。value在下一次ThreadLocalMap調(diào)用set,get清酥,remove的時(shí)候會(huì)被清除扶镀。
比較兩種情況,我們可以發(fā)現(xiàn):由于ThreadLocalMap的生命周期跟Thread一樣長焰轻,如果都沒有手動(dòng)刪除對(duì)應(yīng)key臭觉,都會(huì)導(dǎo)致內(nèi)存泄漏,但是使用弱引用可以多一層保障:弱引用ThreadLocal不會(huì)內(nèi)存泄漏辱志,對(duì)應(yīng)的value在下一次ThreadLocalMap調(diào)用set,get,remove的時(shí)候會(huì)被清除蝠筑。