激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

香港云服务器
服務器之家 - 編程語言 - JAVA教程 - 實例詳解Java中ThreadLocal內存泄露

實例詳解Java中ThreadLocal內存泄露

2020-06-04 11:27daisy JAVA教程

這一篇文章我們來分析一個Java中ThreadLocal內存泄露的案例。分析問題的過程比結果更重要,理論結合實際才能徹底分析出內存泄漏的原因。

案例與分析

問題背景

在 Tomcat 中,下面的代碼都在 webapp 內,會導致WebappClassLoader泄漏,無法被回收。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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被調用過一次,且執行它的線程沒有停止,就會導致WebappClassLoader泄漏。每次你 reload 一下應用,就會多一份WebappClassLoader實例,最后導致 PermGen OutOfMemoryException

解決問題

現在我們來思考一下:為什么上面的ThreadLocal子類會導致內存泄漏?

WebappClassLoader

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

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

也就是說WebappClassLoader是 Tomcat 加載 webapp 的自定義類加載器,每個 webapp 的類加載器都是不一樣的,這是為了隔離不同應用加載的類。

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

我們知道:導致WebappClassLoader泄漏必然是因為它被別的對象強引用了,那么我們可以嘗試畫出它們的引用關系圖。等等!類加載器的作用到底是啥?為什么會被強引用?

類的生命周期與類加載器

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

跟我們這個案例相關的主要是類的卸載:

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

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

      2、加載該類的ClassLoader已經被回收。

      3、該類對應的java.lang.Class對象沒有任何地方被引用,沒有在任何地方通過反射訪問該類的方法。

如果以上三個條件全部滿足,JVM 就會在方法區垃圾回收的時候對類進行卸載,類的卸載過程其實就是在方法區中清空類信息,Java 類的整個生命周期就結束了。

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

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

注意上面這句話,WebappClassLoader如果泄漏了,意味著它加載的類都無法被卸載,這就解釋了為什么上面的代碼會導致 PermGen OutOfMemoryException

關鍵點看下面這幅圖

實例詳解Java中ThreadLocal內存泄露

我們可以發現:類加載器對象跟它加載的 Class 對象是雙向關聯的。這意味著,Class 對象可能就是強引用WebappClassLoader,導致它泄漏的元兇。

引用關系圖

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

實例詳解Java中ThreadLocal內存泄露

下面,我們根據上面的圖來分析WebappClassLoader泄漏的原因。

      1、LeakingServlet持有staticMyThreadLocal,導致myThreadLocal的生命周期跟LeakingServlet類的生命周期一樣長。意味著myThreadLocal不會被回收,弱引用形同虛設,所以當前線程無法通過ThreadLocalMap的防護措施清除counter的強引用。

      2、強引用鏈:thread -> threadLocalMap -> counter -> MyCounter.class -> WebappClassLocader,導致WebappClassLoader泄漏。

總結

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

延伸 · 閱讀

精彩推薦
347
主站蜘蛛池模板: 中文字幕在线网 | 红桃一区 | 在线看免电影网站 | 国产合集91合集久久日 | 亚洲一区二区三区日本久久九 | 日韩欧美电影一区二区三区 | 久久久aa | 4480午夜 | 成人福利软件 | 一区二区三区日韩 | 羞羞视频免费视频欧美 | 亚洲第一成人久久网站 | 爱看久久 | 黄色网址免费进入 | 1314av | 国产精品三级a三级三级午夜 | 久久精品视频首页 | 日日摸夜夜骑 | 成人三级视频在线观看 | av电影院在线观看 | 国产高潮失禁喷水爽到抽搐视频 | 日本一区二区三区高清不卡 | 欧美特黄一级高清免费的香蕉 | 国产乱淫av | 在线播放亚洲视频 | 久久sp| 亚洲操比视频 | 成人午夜一区二区 | 操皮视频 | 久久91精品国产91久久yfo | 爽成人777777婷婷| 日韩做爰视频免费 | 国产精品久久久久久久久久大牛 | 性欧美视频在线观看 | 欧美精品一区自拍a毛片在线视频 | 国产成人av免费 | 中文在线观看视频 | 国产精品美女一区二区 | 91不雅视频 | 亚洲电影免费观看国语版 | 久久久久成人免费 |