程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 實例詳解Java中ThreadLocal內存洩漏

實例詳解Java中ThreadLocal內存洩漏

編輯:關於JAVA

實例詳解Java中ThreadLocal內存洩漏。本站提示廣大學習愛好者:(實例詳解Java中ThreadLocal內存洩漏)文章只能為提供參考,不一定能成為您想要的結果。以下是實例詳解Java中ThreadLocal內存洩漏正文


案例與剖析

成績配景

在 Tomcat 中,上面的代碼都在 webapp 內,會招致WebappClassLoader洩露,沒法被收受接管。

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

症結點看上面這幅圖

 

我們可以發明:類加載器對象跟它加載的 Class 對象是雙向聯系關系的。這意味著,Class 對象能夠就是強援用WebappClassLoader,招致它洩露的首惡。

援用關系圖

懂得類加載器與類的性命周期的關系以後,我們可以開端畫援用關系圖了。(圖中的LeakingServlet.classmyThreadLocal援用畫的不嚴謹,重要是想表達myThreadLocal是類變量的意思)

上面,我們依據下面的圖來剖析WebappClassLoader洩露的緣由。

      1、LeakingServlet持有staticMyThreadLocal,招致myThreadLocal的性命周期跟LeakingServlet類的性命周期一樣長。意味著myThreadLocal不會被收受接管,弱援用形同虛設,所以以後線程沒法經由過程ThreadLocalMap的防護辦法消除counter的強援用。

      2、強援用鏈:thread -> threadLocalMap -> counter -> MyCounter.class -> WebappClassLocader,招致WebappClassLoader洩露。

總結

內存洩露是很難發明的成績,常常因為多方面緣由形成。ThreadLocal因為它與線程綁定的性命周期成了內存洩露的常客,稍有失慎就變成年夜禍。本文只是對一個特定案例的剖析,若能以此觸類旁通,那就是極好的。願望本文對年夜家能有所贊助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved