偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

詳細(xì)解讀ThreadLocal的內(nèi)存泄露

開發(fā) 前端
說到內(nèi)存泄露,阿粉就得說一下,這個(gè)可能對(duì)于初中級(jí)的程序員來說,還是比較陌生的,為什么這么說,是因?yàn)?,JVM 有自己的內(nèi)存回收機(jī)制,所以對(duì)于初中級(jí)的程序員來說,很少有接觸到這個(gè)的,而內(nèi)存泄露的意思呢,就是為程序在申請內(nèi)存后,無法釋放已申請的內(nèi)存空間,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露堆積后果很嚴(yán)重,無論多少內(nèi)存,遲早會(huì)被占光。

?說到內(nèi)存溢出,我相信各位都知道是什么,但是說到內(nèi)存泄露,而且還是 ThreadLocal ,阿粉就得來說一下這個(gè)了,畢竟如果面試的時(shí)候被問到 ThreadLocal 的內(nèi)存泄露,是不是有可能不太了解了呢,今天阿粉來說一下這個(gè) ThreadLocal 的內(nèi)存泄露的原因,以及如何從開發(fā)中去避免這個(gè)問題。

什么是內(nèi)存泄露

說到內(nèi)存泄露,阿粉就得說一下,這個(gè)可能對(duì)于初中級(jí)的程序員來說,還是比較陌生的,為什么這么說,是因?yàn)?,JVM 有自己的內(nèi)存回收機(jī)制,所以對(duì)于初中級(jí)的程序員來說,很少有接觸到這個(gè)的,而內(nèi)存泄露的意思呢,就是為程序在申請內(nèi)存后,無法釋放已申請的內(nèi)存空間,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露堆積后果很嚴(yán)重,無論多少內(nèi)存,遲早會(huì)被占光。

我們也都知道,我們有時(shí)候在定義變量的時(shí)候,就應(yīng)該明白,他需要一段內(nèi)存空間來存儲(chǔ)這個(gè)數(shù)據(jù)信息,而這段內(nèi)存如果一直不被釋放,那么就會(huì)導(dǎo)致,內(nèi)存被占用光,而被占用的這個(gè)對(duì)象,一直不能被回收掉,這就是內(nèi)存泄漏。

圖片

在說 ThreadLocal 的內(nèi)存泄露之前,我們先說說 ThreadLocal 的實(shí)現(xiàn)原理,然后我們再分析,他是泄露的原因是什么?

private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];

for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}

每一個(gè) ThreadLocal 維護(hù)一個(gè) ThreadLocalMap,key為使用 弱引用 的 ThreadLocal 實(shí)例,value為線程變量的副本。

那么什么又是弱引用呢?

引用的話,其實(shí)分為了兩類,既然有弱引用,那么必然的,就會(huì)有強(qiáng)引用,所以我們得區(qū)分開這個(gè)強(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í),無論內(nèi)存是否充足,都會(huì)回收被弱引用關(guān)聯(lián)的對(duì)象。在 java 中,用 java.lang.ref.WeakReference 類來表示。可以在緩存中使用弱引用。

上面阿粉也說了,每一個(gè) ThreadLocal 維護(hù)一個(gè) ThreadLocalMap,key為使用 弱引用 的 ThreadLocal 實(shí)例,value為線程變量的副本。而他們之間的引用關(guān)系如下圖:

圖片

在這個(gè)圖中

  • 實(shí)線表示-強(qiáng)引用
  • 虛線表示-弱引用

從圖中,我們就能分析出這個(gè) ThreadLocal 是怎么出現(xiàn)內(nèi)存泄露的了,我們從圖中能夠看到,ThreadLocalMap 使用 ThreadLocal 的弱引用作為key,如果一個(gè)ThreadLocal不存在外部強(qiáng)引用時(shí),Key(ThreadLocal)勢必會(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

這個(gè)時(shí)候,永遠(yuǎn)無法回收,就會(huì)造成 ThreadLocal 出現(xiàn)內(nèi)存泄露的問題了。

這個(gè)時(shí)候就有讀者會(huì)問,為什么 ThreaLocalMap   使用 ThreadLocal 的弱引用,而不去使用強(qiáng)引用,使用強(qiáng)引用的話,是不是就不會(huì)出現(xiàn)這個(gè)內(nèi)存泄露的問題了呢?

其實(shí)這完全不是一回事,因?yàn)槿绻@時(shí)候 ThreaLocalMap 的 key 為強(qiáng)引用回收 ThreadLocal 時(shí),因?yàn)?ThreadLocalMap 還持有 ThreadLocal 的強(qiáng)引用,如果沒有手動(dòng)刪除,ThreadLocal 不會(huì)被回收,導(dǎo)致Entry內(nèi)存泄漏。

這是使用強(qiáng)引用的時(shí)候,那么使用弱引用的時(shí)候是什么樣的呢?

當(dāng) ThreadLocalMap 的 key 為弱引用回收 ThreadLocal 時(shí),由于 ThreadLocalMap 持有 ThreadLocal 的弱引用,即使沒有手動(dòng)刪除,ThreadLocal 也會(huì)被回收。當(dāng)key為null,在下一次ThreadLocalMap調(diào)用set(),get(),remove()方法的時(shí)候會(huì)被清除value值。

所以這么看下來,反而使用弱引用,卻是更好的為什么呢?

因?yàn)槭褂萌跻每梢远嘁粚颖U希喝跻肨hreadLocal不會(huì)內(nèi)存泄漏,對(duì)應(yīng)的value在下一次 ThreadLocalMap調(diào)用set(),get(),remove()的時(shí)候會(huì)被清除。

因此,ThreadLocal內(nèi)存泄漏的根本原因是:由于 ThreadLocalMap 的生命周期跟 Thread 一樣長,如果沒有手動(dòng)刪除對(duì)應(yīng)key就會(huì)導(dǎo)致內(nèi)存泄漏,而不是因?yàn)槿跻谩?/p>

那么我們繼續(xù)來看看這個(gè) ThreadLocal 中維護(hù)的 ThreadLocalMap 的源碼分析

static class ThreadLocalMap {
//hreadLocalMap中數(shù)據(jù)是存儲(chǔ)在Entry類型數(shù)組的table中的,Entry繼承了WeakReference(弱引用)
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}

**成員變量**
//初始容量
private static final int INITIAL_CAPACITY = 16;

//ThreadLocalMap數(shù)據(jù)真正存儲(chǔ)在table中
private Entry[] table;

//ThreadLocalMap條數(shù)
private int size = 0;

//達(dá)到這個(gè)大小,則擴(kuò)容
private int threshold;

構(gòu)造函數(shù)

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化table數(shù)組,INITIAL_CAPACITY默認(rèn)值為16
table = new Entry[INITIAL_CAPACITY];
//key和16取得哈希值
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//創(chuàng)建節(jié)點(diǎn),設(shè)置key-value
table[i] = new Entry(firstKey, firstValue);
size = 1;
//設(shè)置擴(kuò)容閾值
setThreshold(INITIAL_CAPACITY);
}

我們再來看看他的 remove 方法;

private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//如果threadLocalHashCode計(jì)算出的下標(biāo)找到的key和傳入key不同,則證明出現(xiàn)哈希沖突,則循環(huán)向下查找
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
//如果key相同
if (e.get() == key) {
//刪除當(dāng)前Entry
e.clear();
//清理
expungeStaleEntry(i);
return;
}
}
}

ThreadLocal 中的 ThreadLocalMap 里面還有 set 和 getEntry 方法,還有很多,阿粉就不多介紹了。

那么我們應(yīng)該如何合理的使用 ThreadLocal 才能保證內(nèi)存不泄露呢?

  • 每次使用完ThreadLocal都調(diào)用它的remove()方法清除數(shù)據(jù)
  • 將ThreadLocal變量定義成private static,這樣就一直存在ThreadLocal的強(qiáng)引用,也就能保證任何時(shí)候都能通過ThreadLocal的弱引用訪問到Entry的value值,進(jìn)而清除掉 。

責(zé)任編輯:武曉燕 來源: Java極客技術(shù)
相關(guān)推薦

2024-10-31 09:24:42

2024-10-28 08:15:32

2009-06-16 11:11:07

Java內(nèi)存管理Java內(nèi)存泄漏

2022-10-18 08:38:16

內(nèi)存泄漏線程

2011-07-14 13:50:09

ThreadLocal

2021-04-23 20:59:02

ThreadLocal內(nèi)存

2023-11-03 08:10:49

ThreadLoca內(nèi)存泄露

2021-05-10 11:55:57

ThreadLocal內(nèi)存Java

2010-01-06 19:38:16

.NET Framew

2024-06-24 08:11:37

2013-08-07 10:07:07

Handler內(nèi)存泄露

2009-12-07 15:34:18

PHP類的封裝

2009-12-01 19:28:16

PHP模板

2024-07-29 00:01:00

2010-06-02 13:00:43

Linux 內(nèi)存監(jiān)控

2018-12-14 10:34:57

內(nèi)存性能虛擬機(jī)

2018-07-16 15:05:43

Redis內(nèi)存數(shù)據(jù)庫

2017-12-11 11:00:27

內(nèi)存泄露判斷

2010-01-07 13:17:35

JSON變量

2012-07-02 14:39:59

架構(gòu)敏捷
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)