溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何理解ThreadLocal引發(fā)的內(nèi)存泄露

發(fā)布時(shí)間:2021-10-21 16:08:30 來源:億速云 閱讀:113 作者:iii 欄目:編程語言

這篇文章主要介紹“如何理解ThreadLocal引發(fā)的內(nèi)存泄露”,在日常操作中,相信很多人在如何理解ThreadLocal引發(fā)的內(nèi)存泄露問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對(duì)大家解答”如何理解ThreadLocal引發(fā)的內(nèi)存泄露”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

需求背景

一個(gè)to C的個(gè)人賬戶系統(tǒng),數(shù)據(jù)量大概有2000多W;某天產(chǎn)品突然來說要做一個(gè)to B 商戶賬戶系統(tǒng),馬上一個(gè)星期左右后的大促活動(dòng)就要用,產(chǎn)品跟大老板牛逼已經(jīng)吹出去了,說我們的系統(tǒng)已經(jīng)具備了這個(gè)能力,能夠立馬無縫支持?,F(xiàn)在火急火燎的來找我們,問是不是直接把我們這套to C的賬戶系統(tǒng)的能力提供出去就可以了。

需求分析

首先在這么短的時(shí)間內(nèi)要開發(fā)測(cè)試上線,新做一個(gè)to B的賬戶系統(tǒng)肯定是不現(xiàn)實(shí)的,咱們程序員能力再強(qiáng)也不能流水線生產(chǎn)系統(tǒng)啊。只能依賴當(dāng)前賬戶系統(tǒng)的能力先去支撐這個(gè)業(yè)務(wù)。這兩套賬戶體系底層的核心領(lǐng)域模型可以抽象一致,都包括記賬憑證,記賬主體,記賬流水。不同的地方在于不同的業(yè)務(wù)使用場(chǎng)景下進(jìn)出帳的規(guī)則不一樣,所以規(guī)則層的領(lǐng)域模型需要定制化的配置開發(fā)。同時(shí)目前系統(tǒng)已經(jīng)有2000多W賬戶,日增加流水也是10W級(jí)別的,本身數(shù)據(jù)量已經(jīng)很大,而且業(yè)務(wù)上兩套數(shù)據(jù)會(huì)相互影響,需要在業(yè)務(wù)上做垂直分表,將兩套數(shù)據(jù)隔離。

實(shí)施過程

領(lǐng)域模型和數(shù)據(jù)存儲(chǔ)方案定下來后,馬上就開始實(shí)施了,當(dāng)時(shí)為了更優(yōu)雅的實(shí)現(xiàn),對(duì)目前to C的業(yè)務(wù)影響最小,我們特意在interface入口處統(tǒng)一攔截后在當(dāng)前工作線程的threadlocal加上一個(gè)判斷標(biāo)記,用來識(shí)別是to C的業(yè)務(wù)請(qǐng)求還是to B的業(yè)務(wù)請(qǐng)求,然后業(yè)務(wù)規(guī)則層通過不同的filter進(jìn)行過濾,最后在數(shù)據(jù)庫jdbc層通過不同的標(biāo)記對(duì)不同的表做相應(yīng)的DML處理。方案實(shí)施的很快,兩三天就開發(fā)完提測(cè),可在測(cè)試過程中發(fā)現(xiàn)了兩個(gè)問題,第一個(gè)問題是數(shù)據(jù)有時(shí)候會(huì)無緣無故的錯(cuò)亂,本來是寫到to B表結(jié)構(gòu)的數(shù)據(jù)會(huì)寫到了to C表結(jié)構(gòu)里。而且壓測(cè)時(shí)發(fā)現(xiàn)內(nèi)存監(jiān)控曲線在慢慢的增長,發(fā)生了內(nèi)存泄露。

問題分析

仔細(xì)分析源代碼后發(fā)現(xiàn)問題出現(xiàn)在threadlocal。每個(gè)線程中都有一個(gè)ThreadLocalMap數(shù)據(jù)結(jié)構(gòu),當(dāng)執(zhí)行set方法時(shí),其值是保存在當(dāng)前線程的threadLocals變量中,當(dāng)執(zhí)行g(shù)et方法中,是從當(dāng)前線程的threadLocals變量獲取。所以在線程1中set的值,對(duì)線程2來說是摸不到的,而且在線程2中重新set的話,也不會(huì)影響到線程1中的值,保證了線程之間不會(huì)相互干擾

public void set(T value) {
   Thread t = Thread.currentThread();
   ThreadLocalMap map = getMap(t);
   if (map != null)
       map.set(this, value);
   else
       createMap(t, value);
}

public T get() {
   Thread t = Thread.currentThread();
   ThreadLocalMap map = getMap(t);
   if (map != null) {
       ThreadLocalMap.Entry e = map.getEntry(this);
       if (e != null) {
           @SuppressWarnings("unchecked")
           T result = (T)e.value;
           return result;
      }
  }
   return setInitialValue();
}

ThreadLocalMap getMap(Thread t) {
   return t.threadLocals;
}

//Entry為ThreadLocalMap靜態(tài)內(nèi)部類,對(duì)ThreadLocal的若引用
//同時(shí)讓ThreadLocal和儲(chǔ)值形成key-value的關(guān)系
static class Entry extends WeakReference<ThreadLocal<?>> {
   /** The value associated with this ThreadLocal. */
   Object value;

   Entry(ThreadLocal<?> k, Object v) {
          super(k);
           value = v;
  }
}

內(nèi)存泄露分析

通過代碼可以知道,當(dāng)一個(gè)線程調(diào)用ThreadLocal的set方法設(shè)置變量時(shí)候,當(dāng)前線程的ThreadLocalMap里面就會(huì)存放一個(gè)Entry對(duì)象,這個(gè)記錄的key為ThreadLocal的引用,value則為設(shè)置的值。如果當(dāng)前線程一直存在而沒有調(diào)用ThreadLocal的remove方法,并且這時(shí)候其它地方還是有對(duì)ThreadLocal的引用,則當(dāng)前線程的ThreadLocalMap變量里面會(huì)存在ThreadLocal變量的引用和value對(duì)象的引用是不會(huì)被釋放的,這就會(huì)造成內(nèi)存泄露的。但是考慮如果這個(gè)ThreadLocal變量沒有了其他強(qiáng)依賴,而當(dāng)前線程還存在的情況下,由于線程的ThreadLocalMap里面的key是弱依賴,則當(dāng)前線程的ThreadLocalMap里面的ThreadLocal變量的弱引用會(huì)被在gc的時(shí)候回收,但是對(duì)應(yīng)value還是會(huì)造成內(nèi)存泄露。

數(shù)據(jù)寫錯(cuò)分析

java進(jìn)程是通過外部web容器如tomcat啟動(dòng)的,web容器啟動(dòng)時(shí)會(huì)啟動(dòng)一個(gè)線程池,創(chuàng)建一些核心初始線程,這些線程處理完一個(gè)任務(wù)后會(huì)繼續(xù)處理另外一個(gè)任務(wù)。這個(gè)時(shí)候假如核心線程剛處理完一個(gè)to B的任務(wù),處理完后線程沒有消失繼續(xù)處理一個(gè)to C的任務(wù),這個(gè)時(shí)候threadLocal里的變量標(biāo)記還是直接的標(biāo)記,就會(huì)導(dǎo)致在jdbc層繼續(xù)被路由到to B的表結(jié)構(gòu)里,導(dǎo)致數(shù)據(jù)錯(cuò)誤。

修復(fù)處理

了解清楚threadLoca的存儲(chǔ)結(jié)構(gòu)后,在interface入口處做一個(gè)AOP切片,通過環(huán)繞通知,在方法正式處理前在當(dāng)前工作線程的threadlocal set一個(gè)判斷標(biāo)記,方法處理完成后手動(dòng)remve掉這個(gè)標(biāo)記,保證每次處理后 內(nèi)存正確被釋放。

到此,關(guān)于“如何理解ThreadLocal引發(fā)的內(nèi)存泄露”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI