真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

ThreadLocal在Tomcat中引起內(nèi)存泄露怎么解決

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

成都創(chuàng)新互聯(lián)公司主要從事網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)高碑店,10年網(wǎng)站建設(shè)經(jīng)驗(yàn),價(jià)格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):13518219792

定義

首先,我們要先談一下定義,因?yàn)橐欢讶烁悴欢畠?nèi)存溢出和內(nèi)存泄露的區(qū)別。
內(nèi)存溢出(OutOfMemory):你只有十塊錢,我卻找你要了一百塊。對不起啊,我沒有這么多錢。(給不起)
內(nèi)存泄露(MemoryLeak):你有十塊錢,我找你要一塊。但是無恥的博主,不把錢還你了。(沒退還)
關(guān)系:多次的內(nèi)存泄露,會導(dǎo)致內(nèi)存溢出。(博主不要臉的找你多要幾次錢,你就沒錢了,就是這個(gè)道理。)

危害

ok,大家在項(xiàng)目中有沒遇到過java程序越來越卡的情況。
因?yàn)閮?nèi)存泄露,會導(dǎo)致頻繁的Full GC,而Full GC 又會造成程序停頓,最后Crash了。因此,你會感覺到你的程序越來越卡,越來越卡,然后你就被產(chǎn)品經(jīng)理鄙視了。順便提一下,我們之所以JVM調(diào)優(yōu),就是為了減少Full GC的出現(xiàn)。
我記得,我曾經(jīng)有一次,就遇到項(xiàng)目剛上線的時(shí)候好好的。結(jié)果隨著時(shí)間的堆積,報(bào)了OutOfMemoryError: PermGen space。
說到這個(gè)PermGen space,突然間,一陣洪荒之力,從博主體內(nèi)噴涌而出,一定要介紹一下這個(gè)方法區(qū),不過點(diǎn)到為止,畢竟這不是在講《jvm從入門到放棄》。
方法區(qū):出自java虛擬機(jī)規(guī)范, 可供各條線程共享運(yùn)行時(shí)內(nèi)存區(qū)域。它存儲了每一個(gè)類的結(jié)構(gòu)信息,例如運(yùn)行時(shí)常量池(Runtime Constant Pool)、字段和方法數(shù)據(jù)、構(gòu)造函數(shù)和普通方法的字節(jié)碼內(nèi)容。
上面講的是規(guī)范,在不同虛擬機(jī)里頭實(shí)現(xiàn)是不一樣的,最典型的就是永久代(PermGen space)元空間(Metaspace)。

jdk1.8以前:實(shí)現(xiàn)方法區(qū)的叫永久代。因?yàn)樵诤芫眠h(yuǎn)以前,java覺得類幾乎是靜態(tài)的,并且很少被卸載和回收,所以給了一個(gè)永久代的雅稱。因此,如果你在項(xiàng)目中,發(fā)現(xiàn)堆和永久代一直在不斷增長,沒有下降趨勢,回收的速度根本趕不上增長的速度,不用說了,這種情況基本可以確定是內(nèi)存泄露。

jdk1.8以后:實(shí)現(xiàn)方法區(qū)的叫元空間。Java覺得對永久代進(jìn)行調(diào)優(yōu)是很困難的。永久代中的元數(shù)據(jù)可能會隨著每一次Full GC發(fā)生而進(jìn)行移動。并且為永久代設(shè)置空間大小也是很難確定的。因此,java決定將類的元數(shù)據(jù)分配在本地內(nèi)存中,元空間的最大可分配空間就是系統(tǒng)可用內(nèi)存空間。這樣,我們就避開了設(shè)置永久代大小的問題。但是,這種情況下,一旦發(fā)生內(nèi)存泄露,會占用你的大量本地內(nèi)存。如果你發(fā)現(xiàn),你的項(xiàng)目中本地內(nèi)存占用率異常高。嗯,這就是內(nèi)存泄露了。

如何排查

(1)通過jps查找java進(jìn)程id。
(2)通過top -p [pid]發(fā)現(xiàn)內(nèi)存占用達(dá)到了最大值
(3)jstat -gccause pid 20000 每隔20秒輸出Full GC結(jié)果
(4)發(fā)現(xiàn)Full GC次數(shù)太多,基本就是內(nèi)存泄露了。生成dump文件,借助工具分析是哪個(gè)對象太多了?;灸芏ㄎ坏絾栴}在哪。

實(shí)例

在stackoverflow上,有一個(gè)問題,如下所示

I just had an interview, and I was asked to create a memory leak with Java. Needless to say I felt pretty dumb having no clue on how to even start creating one.

大致就是,因?yàn)槊嬖囆枰謱懸欢蝺?nèi)存泄露的程序,然后提問的人突然懵逼了,于是很多大佬紛紛給出回答。
案例一
此例子出自《算法》(第四版)一書,我簡化了一下

    class stack{    
        Object data[1000];    
        int top = 0;    
        public void push(Object o){        
            data[top++] = o;   
        }    
        public Object pop(Object o){ 
            return data[--top];
        }
    }

當(dāng)數(shù)據(jù)從棧里面彈出來之后,data數(shù)組還一直保留著指向元素的指針。那么就算你把棧pop空了,這些元素占的內(nèi)存也不會被回收的。
解決方案就是

    public Object pop(Object o){ 
        Object result = data[--top];
        data[top] = null;
        return result;
    }

案例二
這個(gè)其實(shí)是一堆例子,這些例子造成內(nèi)存泄露的原因都是類似的,就是不關(guān)閉流,具體的,可以是文件流,socket流,數(shù)據(jù)庫連接流,等等
具體如下,沒關(guān)文件流

try {
    BufferedReader br = new BufferedReader(new FileReader(inputFile));
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

再比如,沒關(guān)閉連接

try {
    Connection conn = ConnectionFactory.getConnection();
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

解決方案就是。。。嗯,大家應(yīng)該都會。。你敢說你不會調(diào)close()方法。
案例三
講這個(gè)例子前,大家對ThreadLocalTomcat中引起內(nèi)存泄露有了解么。不過,我要說一下,這個(gè)泄露問題,和ThreadLocal本身關(guān)系不大,我看了一下官網(wǎng)給的例子,基本都是屬于使用不當(dāng)引起的。
在Tomcat的官網(wǎng)上,記錄了這個(gè)問題。地址是:https://wiki.apache.org/tomcat/MemoryLeakProtection
不過,官網(wǎng)的這個(gè)例子,可能不好理解,我們略作改動。

public class HelloServlet extends HttpServlet{
    private static final long serialVersionUID = 1L;
    static class LocalVariable {
        private Long[] a = new Long[1024 * 1024 * 100];
    }
    final static ThreadLocal localVariable = new ThreadLocal();
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        localVariable.set(new LocalVariable());
    }
}

再來看下conf下sever.xml配置

  
    

線程池最大線程為150個(gè),最小線程為4個(gè)

Tomcat中Connector組件負(fù)責(zé)接受并處理請求,每來一個(gè)請求,就會去線程池中取一個(gè)線程。
在訪問該servlet時(shí),ThreadLocal變量里面被添加了new LocalVariable()實(shí)例,但是沒有被remove,這樣該變量就隨著線程回到了線程池中。另外多次訪問該servlet可能用的不是工作線程池里面的同一個(gè)線程,這會導(dǎo)致工作線程池里面多個(gè)線程都會存在內(nèi)存泄露。

另外,servletdoGet方法里面創(chuàng)建new LocalVariable()的時(shí)候使用的是webappclassloader。
那么
LocalVariable對象沒有釋放 -> LocalVariable.class沒有釋放 ->webappclassloader沒有釋放 -> webappclassloader加載的所有類也沒有被釋放,也造成了內(nèi)存泄露。

除此之外,你在eclipse中,做一個(gè)reload操作,工作線程池里面的線程還是一直存在的,并且線程里面的threadLocal變量并沒有被清理。而reload的時(shí)候,又會新構(gòu)建一個(gè)webappclassloader,重復(fù)上述步驟。多reload幾次,就內(nèi)存溢出。
不過Tomcat7.0以后,你每做一次reload,會清理工作線程池中線程的threadLocals變量。因此,這個(gè)問題在tomcat7.0后,不會存在。

ps:ThreadLocal的使用在Tomcat的服務(wù)環(huán)境下要注意,并非每次web請求時(shí)候程序運(yùn)行的ThreadLocal都是唯一的。ThreadLocal的什么生命周期不等于一次Request的生命周期。ThreadLocal與線程對象緊密綁定的,由于Tomcat使用了線程池,線程是可能存在復(fù)用情況。

到此,關(guān)于“ThreadLocal在Tomcat中引起內(nèi)存泄露怎么解決”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!


分享文章:ThreadLocal在Tomcat中引起內(nèi)存泄露怎么解決
網(wǎng)站地址:http://weahome.cn/article/gggccs.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部