国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > Java > 正文

實(shí)例詳解Java中ThreadLocal內(nèi)存泄露

2019-11-26 13:58:57
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

案例與分析

問題背景

在 Tomcat 中,下面的代碼都在 webapp 內(nèi),會(huì)導(dǎo)致WebappClassLoader泄漏,無(wú)法被回收。

public class MyCounter {    private int count = 0;    public void increment() {        count++;    }    public int getCount() {        return count;    }}public class MyThreadLocal extends ThreadLocal<MyCounter> {}public class LeakingServlet extends HttpServlet {    private static MyThreadLocal myThreadLocal = new MyThreadLocal();    protected void doGet(HttpServletRequest request,            HttpServletResponse response) throws ServletException, IOException {        MyCounter counter = myThreadLocal.get();        if (counter == null) {            counter = new MyCounter();            myThreadLocal.set(counter);        }        response.getWriter().println(                "The current thread served this servlet " + counter.getCount()                        + " times");        counter.increment();    }}

上面的代碼中,只要LeakingServlet被調(diào)用過(guò)一次,且執(zhí)行它的線程沒有停止,就會(huì)導(dǎo)致WebappClassLoader泄漏。每次你 reload 一下應(yīng)用,就會(huì)多一份WebappClassLoader實(shí)例,最后導(dǎo)致 PermGen OutOfMemoryException

解決問題

現(xiàn)在我們來(lái)思考一下:為什么上面的ThreadLocal子類會(huì)導(dǎo)致內(nèi)存泄漏?

WebappClassLoader

首先,我們要搞清楚WebappClassLoader是什么鬼?

對(duì)于運(yùn)行在 Java EE容器中的 Web 應(yīng)用來(lái)說(shuō),類加載器的實(shí)現(xiàn)方式與一般的 Java 應(yīng)用有所不同。不同的 Web 容器的實(shí)現(xiàn)方式也會(huì)有所不同。以 Apache Tomcat 來(lái)說(shuō),每個(gè) Web 應(yīng)用都有一個(gè)對(duì)應(yīng)的類加載器實(shí)例。該類加載器也使用代理模式,所不同的是它是首先嘗試去加載某個(gè)類,如果找不到再代理給父類加載器。這與一般類加載器的順序是相反的。這是 Java Servlet 規(guī)范中的推薦做法,其目的是使得 Web 應(yīng)用自己的類的優(yōu)先級(jí)高于 Web 容器提供的類。這種代理模式的一個(gè)例外是:Java 核心庫(kù)的類是不在查找范圍之內(nèi)的。這也是為了保證 Java 核心庫(kù)的類型安全。

也就是說(shuō)WebappClassLoader是 Tomcat 加載 webapp 的自定義類加載器,每個(gè) webapp 的類加載器都是不一樣的,這是為了隔離不同應(yīng)用加載的類。

那么WebappClassLoader的特性跟內(nèi)存泄漏有什么關(guān)系呢?目前還看不出來(lái),但是它的一個(gè)很重要的特點(diǎn)值得我們注意:每個(gè) webapp 都會(huì)自己的WebappClassLoader,這跟 Java 核心的類加載器不一樣。

我們知道:導(dǎo)致WebappClassLoader泄漏必然是因?yàn)樗粍e的對(duì)象強(qiáng)引用了,那么我們可以嘗試畫出它們的引用關(guān)系圖。等等!類加載器的作用到底是啥?為什么會(huì)被強(qiáng)引用?

類的生命周期與類加載器

要解決上面的問題,我們得去研究一下類的生命周期和類加載器的關(guān)系。

跟我們這個(gè)案例相關(guān)的主要是類的卸載:

在類使用完之后,如果滿足下面的情況,類就會(huì)被卸載:

      1、該類所有的實(shí)例都已經(jīng)被回收,也就是 Java 堆中不存在該類的任何實(shí)例。

      2、加載該類的ClassLoader已經(jīng)被回收。

      3、該類對(duì)應(yīng)的java.lang.Class對(duì)象沒有任何地方被引用,沒有在任何地方通過(guò)反射訪問該類的方法。

如果以上三個(gè)條件全部滿足,JVM 就會(huì)在方法區(qū)垃圾回收的時(shí)候?qū)︻愡M(jìn)行卸載,類的卸載過(guò)程其實(shí)就是在方法區(qū)中清空類信息,Java 類的整個(gè)生命周期就結(jié)束了。

由Java虛擬機(jī)自帶的類加載器所加載的類,在虛擬機(jī)的生命周期中,始終不會(huì)被卸載。Java虛擬機(jī)自帶的類加載器包括根類加載器、擴(kuò)展類加載器和系統(tǒng)類加載器。Java虛擬機(jī)本身會(huì)始終引用這些類加載器,而這些類加載器則會(huì)始終引用它們所加載的類的Class對(duì)象,因此這些Class對(duì)象始終是可觸及的。

由用戶自定義的類加載器加載的類是可以被卸載的。

注意上面這句話,WebappClassLoader如果泄漏了,意味著它加載的類都無(wú)法被卸載,這就解釋了為什么上面的代碼會(huì)導(dǎo)致 PermGen OutOfMemoryException。

關(guān)鍵點(diǎn)看下面這幅圖

 

我們可以發(fā)現(xiàn):類加載器對(duì)象跟它加載的 Class 對(duì)象是雙向關(guān)聯(lián)的。這意味著,Class 對(duì)象可能就是強(qiáng)引用WebappClassLoader,導(dǎo)致它泄漏的元兇。

引用關(guān)系圖

理解類加載器與類的生命周期的關(guān)系之后,我們可以開始畫引用關(guān)系圖了。(圖中的LeakingServlet.classmyThreadLocal引用畫的不嚴(yán)謹(jǐn),主要是想表達(dá)myThreadLocal是類變量的意思)

下面,我們根據(jù)上面的圖來(lái)分析WebappClassLoader泄漏的原因。

      1、LeakingServlet持有staticMyThreadLocal,導(dǎo)致myThreadLocal的生命周期跟LeakingServlet類的生命周期一樣長(zhǎng)。意味著myThreadLocal不會(huì)被回收,弱引用形同虛設(shè),所以當(dāng)前線程無(wú)法通過(guò)ThreadLocalMap的防護(hù)措施清除counter的強(qiáng)引用。

      2、強(qiáng)引用鏈:thread -> threadLocalMap -> counter -> MyCounter.class -> WebappClassLocader,導(dǎo)致WebappClassLoader泄漏。

總結(jié)

內(nèi)存泄漏是很難發(fā)現(xiàn)的問題,往往由于多方面原因造成。ThreadLocal由于它與線程綁定的生命周期成為了內(nèi)存泄漏的???,稍有不慎就釀成大禍。本文只是對(duì)一個(gè)特定案例的分析,若能以此舉一反三,那便是極好的。希望本文對(duì)大家能有所幫助。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 黑河市| 阳曲县| 靖边县| 孟连| 元阳县| 哈巴河县| 贺州市| 永修县| 恭城| 遂川县| 类乌齐县| 武定县| 五寨县| 和平区| 朔州市| 阿合奇县| 桦川县| 右玉县| 当阳市| 通河县| 枞阳县| 边坝县| 六安市| 陆川县| 柳林县| 杭州市| 安西县| 洮南市| 镇安县| 长子县| 莱西市| 江安县| 涞水县| 苏州市| 来凤县| 东乡县| 三江| 周口市| 武强县| 九江县| 宣化县|