java thread 内存泄露_Java ThreadLocal 内存泄露问题分析及解决方法。
前言
在分析ThreadLocal導(dǎo)致的內(nèi)存泄露前,需要普及了解一下內(nèi)存泄露、強(qiáng)引用與弱引用以及GC回收機(jī)制,這樣才能更好的分析為什么ThreadLocal會(huì)導(dǎo)致內(nèi)存泄露呢?更重要的是知道該如何避免這樣情況發(fā)生,增強(qiáng)系統(tǒng)的健壯性。
內(nèi)存泄露
內(nèi)存泄露為程序在申請(qǐng)內(nèi)存后,無(wú)法釋放已申請(qǐng)的內(nèi)存空間,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露堆積后果很嚴(yán)重,無(wú)論多少內(nèi)存,遲早會(huì)被占光,
廣義并通俗的說(shuō),就是:不再會(huì)被使用的對(duì)象或者變量占用的內(nèi)存不能被回收,就是內(nèi)存泄露。
強(qiáng)引用與弱引用
強(qiáng)引用,使用最普遍的引用,一個(gè)對(duì)象具有強(qiáng)引用,不會(huì)被垃圾回收器回收。當(dāng)內(nèi)存空間不足,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤,使程序異常終止,也不回收這種對(duì)象。
如果想取消強(qiáng)引用和某個(gè)對(duì)象之間的關(guān)聯(lián),可以顯式地將引用賦值為null,這樣可以使JVM在合適的時(shí)間就會(huì)回收該對(duì)象。
弱引用,JVM進(jìn)行垃圾回收時(shí),無(wú)論內(nèi)存是否充足,都會(huì)回收被弱引用關(guān)聯(lián)的對(duì)象。在java中,用java.lang.ref.WeakReference類來(lái)表示。可以在緩存中使用弱引用。
GC回收機(jī)制-如何找到需要回收的對(duì)象
JVM如何找到需要回收的對(duì)象,方式有兩種:引用計(jì)數(shù)法:每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性,新增一個(gè)引用時(shí)計(jì)數(shù)加1,引用釋放時(shí)計(jì)數(shù)減1,計(jì)數(shù)為0時(shí)可以回收,
可達(dá)性分析法:從 GC Roots 開(kāi)始向下搜索,搜索所走過(guò)的路徑稱為引用鏈。當(dāng)一個(gè)對(duì)象到 GC Roots 沒(méi)有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的,那么虛擬機(jī)就判斷是可回收對(duì)象。引用計(jì)數(shù)法,可能會(huì)出現(xiàn)A 引用了 B,B 又引用了 A,這時(shí)候就算他們都不再使用了,但因?yàn)橄嗷ヒ?計(jì)數(shù)器=1 永遠(yuǎn)無(wú)法被回收。
ThreadLocal的內(nèi)存泄露分析
先從前言的了解了一些概念(已懂忽略),接下來(lái)我們開(kāi)始正式的來(lái)理解ThreadLocal導(dǎo)致的內(nèi)存泄露的解析。
實(shí)現(xiàn)原理
static?class?ThreadLocalMap?{
static?class?Entry?extends?WeakReference>?{
/**?The?value?associated?with?this?ThreadLocal.?*/
Object?value;
Entry(ThreadLocal>?k,?Object?v)?{
super(k);
value?=?v;
}
}
...
}
ThreadLocal的實(shí)現(xiàn)原理,每一個(gè)Thread維護(hù)一個(gè)ThreadLocalMap,key為使用弱引用的ThreadLocal實(shí)例,value為線程變量的副本。這些對(duì)象之間的引用關(guān)系如下,
實(shí)心箭頭表示強(qiáng)引用,空心箭頭表示弱引用
ThreadLocal 內(nèi)存泄漏的原因
從上圖中可以看出,hreadLocalMap使用ThreadLocal的弱引用作為key,如果一個(gè)ThreadLocal不存在外部強(qiáng)引用時(shí),Key(ThreadLocal)勢(shì)必會(huì)被GC回收,這樣就會(huì)導(dǎo)致ThreadLocalMap中key為null, 而value還存在著強(qiáng)引用,只有thead線程退出以后,value的強(qiáng)引用鏈條才會(huì)斷掉。
但如果當(dāng)前線程再遲遲不結(jié)束的話,這些key為null的Entry的value就會(huì)一直存在一條強(qiáng)引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
永遠(yuǎn)無(wú)法回收,造成內(nèi)存泄漏。
那為什么使用弱引用而不是強(qiáng)引用??
我們看看Key使用的
key 使用強(qiáng)引用
當(dāng)hreadLocalMap的key為強(qiáng)引用回收ThreadLocal時(shí),因?yàn)門hreadLocalMap還持有ThreadLocal的強(qiáng)引用,如果沒(méi)有手動(dòng)刪除,ThreadLocal不會(huì)被回收,導(dǎo)致Entry內(nèi)存泄漏。
key 使用弱引用
當(dāng)ThreadLocalMap的key為弱引用回收ThreadLocal時(shí),由于ThreadLocalMap持有ThreadLocal的弱引用,即使沒(méi)有手動(dòng)刪除,ThreadLocal也會(huì)被回收。當(dāng)key為null,在下一次ThreadLocalMap調(diào)用set(),get(),remove()方法的時(shí)候會(huì)被清除value值。
ThreadLocalMap的remove()分析
在這里只分析remove()方式,其他的方法可以查看源碼進(jìn)行分析:
private?void?remove(ThreadLocal>?key)?{
//使用hash方式,計(jì)算當(dāng)前ThreadLocal變量所在table數(shù)組位置
Entry[]?tab?=?table;
int?len?=?tab.length;
int?i?=?key.threadLocalHashCode?&?(len-1);
//再次循環(huán)判斷是否在為ThreadLocal變量所在table數(shù)組位置
for?(Entry?e?=?tab[i];
e?!=?null;
e?=?tab[i?=?nextIndex(i,?len)])?{
if?(e.get()?==?key)?{
//調(diào)用WeakReference的clear方法清除對(duì)ThreadLocal的弱引用
e.clear();
//清理key為null的元素
expungeStaleEntry(i);
return;
}
}
}
再看看清理key為null的元素expungeStaleEntry(i):
總結(jié)
以上是生活随笔為你收集整理的java thread 内存泄露_Java ThreadLocal 内存泄露问题分析及解决方法。的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java postdelayed_And
- 下一篇: java基本数据类型与封装类 示例_Ja