分析PermGen上存放的被Classloader所加載的類實踐

原創作品,允許轉載,轉載時請務必以超鏈接形式標明文章 原始出處 、作者信息和本聲明。否則將追究法律責任。http://supercharles888.blog.51cto.com/609344/1347615

引入:

上文中我們用MAT工具去分析了關於堆內存的很多細節問題,而內存除了堆內存以外還有非堆內存(包括棧和方法區),而方法區PermGen,它主要存放了兩種東西,一種是被ClassLoader加載的類,一種是字符串常量,我們這篇文章着重分析PermGen上被ClassLoader所加載的類。


實踐:


思路:

因爲OutOfMemoryError時會生成HeapDump文件

(通過命令行參數 -XX:+HeapDumpOnOutOfMemoryError) ,而這個HeapDump文件不僅會包含堆信息,而且會包含Perm上的被ClassLoader所加載的Class的信息,所以我們實踐的思想就是先構造一個場景,通過無限循環讓無窮多個ClassLoader加載類,從而讓PermGen OOM,然後藉助工具分析HeapDump。



準備:

爲此,我們加入以下命令行選項:

wKioOVLE1gODGgU3AAA0APyDK2Y467.jpg

我這裏分別解釋下:

-XX:PermSize=16M 和-XX:MaxPermSize=32M的目的是讓Perm儘可能設置小,從而縮短我們做實驗的時間。

-XX:+HeapDumpOnOutOfMemoryError的目的是爲了在OOM時候生成一個heapDump文件,而這個文件我們是可以通過工具來分析Perm上的被加載的類的信息的。

-XX:-ClassUnloading的目的是讓HotSpot VM在做Full GC時候不會對PermGen上的加載的類進行GC,從而縮短做實驗時間 (也就是讓被加載的類佔用的內存只增不減,從而更容易OOM)

-verbose 不用多說了,讓我們能看到執行,從而知道ClassLoader正在加載類



代碼:

按照上面思路,我們寫了一個程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
     * 讓無限多個Classloader去加載某個類,因爲不同的ClassLoader加載的類是不同的,所以加載後的Class會放在
     * 內存的Perm區,從而最終會導致內存溢出,我們可以吧內存Perm區設置小點,然後加載一個比較大的類(比如多於1000行)
     * 來縮短我們的實驗時間
     * @throws Exception
     */
    public static void makePermOutOfMemory1() throws Exception{
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
        //用URLClassloader可以加載任意目錄/JAR包下的類的字節碼文件
        String path="D:/Charles/Workspace2/MemoryLeakResearch/classloaded/commons-codec-1.4.jar";
        URL url= new File(path).toURL(); 
        URL[] urls = {url};
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
        //無限創建新的URLClassLoader,並且讓其加載指定的一個大類
        while(true){
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
            URLClassLoader cl = new URLClassLoader(urls);
            cl.loadClass("org.apache.commons.codec.binary.Base64");  
        }    
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        
    }


程序很簡單,就是在無限循環中創建無數多個URLClassLoader,然後讓他們去加載commons-codec-1.4.jar包中的Base64類,這個類有1000多行,算是個比較大的類,所以導致OOM發生不會花費太多時間。

這裏我們用URLClassLoader的目的是它可以加載任意目錄位置或者JAR包中的類,我們只是爲了展示這個代碼的通用性,而不要讓類侷限於本項目。



結果:

運行了十幾秒鐘後,果然OOM 發生了:

wKioOVLE2DfgydlLAAFHECO4pog137.jpg

和我們設想的一樣,PermGen 溢出,並且生成了一個heap dump文件。



分析結果:

我們依然用MAT工具來分析這個heap dump文件,在"Java Basics"->"Class Loader Explorer"下,我們看到了PermGen中被各個類加載器中加載的類的情況:

wKioJlLE2VKRD9VNAADZc-5YdAg388.jpg

從這裏可以看出,這裏有 2048-3=2045個java.net.URLClassLoader 被創建,這就是我們在無限循環中無限創建的URLClassLoader,每個ClassLoader載入了5個類,而被加載的類都放在了PermGen上,所以導致了PermGen的OutOfMemory.


我們驗證下,隨便打開某個java.net.URLClassLoader:

wKioOVLE2gDySPfmAACgPxX9Z0M938.jpg

可以發現的確每個ClassLoader加載了5個類,分別是BinaryDecoder,BinaryEncoder,Decoder,Encoder,Base64 .

結合我們自己寫的代碼,我們是顯式的讓新建的URLClassLoader去loadClass這個Base64類的:

1
2
URLClassLoader cl = new URLClassLoader(urls);
            cl.loadClass("org.apache.commons.codec.binary.Base64");

而Base64類會實現接口BinaryDecoder和BinaryEncoder:

wKioJlLE2pTDYMdLAAA77NUH3SU606.jpg

所以同一個URLClassLoader也會去加載BinaryDecoder和BinaryEncoder。


而BinaryDecoder接口是繼承自Decoder接口的,BinaryEncoder接口是繼承自Encoder接口的:

wKioJlLE2vyD9otLAABExUX-ttA887.jpg

所以同一個URLClassLoader也會去加載Decoder和Encoder.



總結:

(1)PermGen是JAVA規範中的”方法區”,它主要放兩部分內容,一塊是被Classloader加載的類,另一塊是字符串常量區。

(2)PermGen的ClassLoader加載的類信息,會放入heap dump文件中,而字符串常量的信息,不會放入heap dump文件中。

(3)默認HotSpot VM在做Full GC時候,回收的是Heap上的內存+PermGen上的加載的類+PermGen上的字符串常量,但是如果配置了vm參數 -XX:-ClassUnloading後 ,則只回收Heap上的內存+PermGen上的字符串常量

(3)對於Classloader加載類順序,符合“雙親委派”的加載鏈模式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章