<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ThreadLocal導(dǎo)致內(nèi)存泄漏排查小記

          共 4387字,需瀏覽 9分鐘

           ·

          2021-04-02 13:34

          背景描述

                公司sso域名變動(dòng),所有涉及的產(chǎn)品都要修改相關(guān)的配置。配置修改好之后,運(yùn)行期間發(fā)現(xiàn)業(yè)務(wù)系統(tǒng)不穩(wěn)定,出現(xiàn)了很多json解析異常。但是隨著sso那邊問題得到修改,我們自己的產(chǎn)品也逐漸穩(wěn)定起來(lái),但查看日志發(fā)現(xiàn)多條內(nèi)存泄露的日志,于是本著學(xué)習(xí)的心態(tài),對(duì)具體的原因進(jìn)行了粗略的分析,最終得出的結(jié)論是異常導(dǎo)致threadLocal.remove()方法沒有執(zhí)行,最后內(nèi)存泄漏了,以下是本人定位問題的過(guò)程。

          報(bào)錯(cuò)日志

          6:21:26.656 嚴(yán)重 [Thread-219] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaksThe web application [ttt] created a ThreadLocal withkey of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@1201c9a0]) and a value of type[tt.zzz.loghelper.model.ActionLog] (value []) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.

          翻譯和分析

               這個(gè)threadlocal移除不了,直到項(xiàng)目死了都還沒移除掉。具體的異常發(fā)起者是這個(gè)catalina的loader,具體的方法就是checkThreadLocalMapForLeaks (檢測(cè)線程的threadlocal是否有泄露),大概說(shuō)一下就是就是說(shuō)檢測(cè)這個(gè)線程的threadlocal,然后發(fā)現(xiàn)線程中的threadlocal有值,然后就拋出了內(nèi)存泄露這個(gè)異常。大概猜測(cè)一下應(yīng)該是是tomcat在處理請(qǐng)求的時(shí)候,因?yàn)橐獜木€程池中獲取線程,然后讓這個(gè)線程去跑請(qǐng)求,但是通過(guò)這個(gè)檢測(cè)方法檢測(cè)一下,發(fā)現(xiàn)當(dāng)前獲取的這個(gè)線程的threadLocal沒有釋放掉。我們當(dāng)時(shí)說(shuō)threadlocal是一個(gè)弱引用,我們說(shuō)弱引用只會(huì)在內(nèi)存不夠的時(shí)候,jvm才會(huì)回收它。而我們的thredlocal保存的map映射關(guān)系就是保存在這里的弱引用中,意思是如果我們不顯式的通過(guò)remove()方法去移除弱引用中的值,那么就會(huì)存在內(nèi)存泄露的問題。所以這個(gè)報(bào)錯(cuò)日志的核心就是沒有走threadlocal.remove()方法。

          定位問題代碼

                 日志收集上,我們采用了之前老員工寫的日志切面,大概得代碼如下:

          @Aspectpublic class WebLogAspect {    private static final Logger log = LoggerFactory.getLogger(WebLogAspect.class);    private ThreadLocalthreadLocal = new ThreadLocal();    @Autowired    private LogHelperProperties logHelperProperties;    @Autowired    private LogService logService;        public WebLogAspect() {    }
          @Pointcut("@annotation(ttt .tt.loghelper.aspect.WebLog)") public void webLog() { }//執(zhí)行之前 @Before("webLog()") public void doBefore(JoinPoint joinPoint) { ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes(); if (attributes != null) { HttpServletRequest request = attributes.getRequest(); ActionLog actionLog = new ActionLog();//設(shè)置threadLocal變量 this.threadLocal.set(actionLog);        }    }//這里回環(huán)日志 @Around("webLog()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { //這里繼續(xù)執(zhí)行我們自己的函數(shù) Object result = proceedingJoinPoint.proceed(); ActionLog actionLog = (ActionLog)this.threadLocal.get(); //這里對(duì)threadLocal進(jìn)行remove操作,這里應(yīng)該沒有執(zhí)行? this.threadLocal.remove(); //寫日志 this.logService.writeActionLog(actionLog); return result;    }}

                 通過(guò)查看代碼我們知道這塊的  this.threadLocal.remove();應(yīng)該是沒有執(zhí)行的,那么沒有執(zhí)行的原因就是異常了。為此作者編寫了如下的代碼測(cè)試了一下:

          public class TestThread extends  Thread{
          private static ThreadLocalmyThreadThreadLocal=new ThreadLocal<>();
          public static void main(String[] args) { MyThread thread=new MyThread(); thread.setName("tianjl"); //設(shè)置threadLocal變量 myThreadThreadLocal.set(thread); try{ //里邊拋異常 doSomeThing(); //下邊的代碼是不執(zhí)行的,也就是this.threadLocal.remove();不執(zhí)行 System.out.println(myThreadThreadLocal.get().toString()); myThreadThreadLocal.remove(); }catch (Exception e){ e.printStackTrace(); } //這里可以獲取到本該remove的threadlocal的值 System.out.println(myThreadThreadLocal.get().toString()); }
          private static void doSomeThing() throws Exception { throw new Exception("測(cè)試異常"); }}

          執(zhí)行的效果如下

          結(jié)論和解決方法

                 根據(jù)SSO的變動(dòng)我們知道,sso異常導(dǎo)致了線程直接跳出方法,使得函數(shù)沒有執(zhí)行threadlocal.remove()方法。造成了threadlocal中的值沒有清理,最終導(dǎo)致tomcat在檢測(cè)線程的threadlocal的時(shí)候發(fā)現(xiàn)有內(nèi)存泄露,最后直接拋異常了。具體的解決方法就是將threadlocal.remove()放到finally中去。  

          @Around("webLog()")public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {    try{       //這里繼續(xù)執(zhí)行我們自己的函數(shù)        Object result = proceedingJoinPoint.proceed();        ActionLog actionLog = (ActionLog)this.threadLocal.get();        //寫日志        this.logService.writeActionLog(actionLog);        return result;      }finally{       //這里對(duì)threadLocal進(jìn)行remove操作,這里應(yīng)該沒有執(zhí)行?        this.threadLocal.remove();      }}

          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人网在线影片 | 国产精品无码在线 | 青娱乐青青草论坛在线 | 亚洲中文字幕第一 | 青青青操逼电影 |