《深入理解Java虛擬機》讀後總結 (二)JVM內存分配

(一)JVM內存模型
(二)JVM內存分配
(三)JVM內存監控
(四)JVM垃圾回收

2 JVM內存分配

使用對JVM不同內存區域灌入數據,導致相關區域內存溢出,來驗證JVM內存分配
先看一個經典問題:

String s1 = "小金子(aub)";  
String s2 = "小金子(aub)";  
String s3 = "小金子" + "(aub)";  
String s4 = new String("小金子(aub)");  
String s5 = "小金子" + new String("(aub)");  
String s6 = s4.intern();  
System.out.println("s1 == s2: " + (s1 == s2));//true;  
System.out.println("s1 == s3: " + (s1 == s3));//true;  
System.out.println("s2 == s3: " + (s2 == s3));//true;  
System.out.println("s1 == s4: " + (s1 == s4));//false;  
System.out.println("s1 == s5: " + (s1 == s5));//false;  
System.out.println("s4 == s5: " + (s4 == s5));//false;  
System.out.println("s1 == s6: " + (s1 == s6));//true;  

原因就在與String對象特殊的內存分配方式:(Strings pool是JVM內存中運行時常量池的一部分)
1. String s1 = new String(“小金子(aub)”);
2. String s2 = “小金子(aub)”;
3. String s3 = “小金子” + “(aub)”;
雖然兩個語句都是返回一個String對象的引用,但是jvm對兩者的處理方式是不一樣的。
對於第一種,jvm會馬上在heap中創建一個String對象,然後將該對象的引用返回給用戶。
對於第二種,jvm首先會在內部維護的strings pool中通過String的 equels 方法查找是對象池中是否存放有該String對象,如果有,則返回已有的String對象給用戶,而不會在heap中重新創建一個新的String對象;如果對象池中沒有該String對象,jvm則在heap中創建新的String對象,將其引用返回給用戶,同時將該引用添加至strings pool中。
注意:使用第一種方法創建對象時,jvm是不會主動把該對象放到strings pool裏面的,除非程序調用 String的intern方法
對於第三種,jvm會進行“+”運算符號的優化,兩遍都是字符串常量會做類似於第二種的處理,如果“+”任意一邊是一個變量,就會做類似第一種的處理。

2.1 JVM棧和Native Method棧內存分配:

JAVA中八個基本類型數據,在運行時都是分配在棧中的。在棧上分配的內存,隨着數據的進棧出棧,方法運行完畢,或則線程結束時,自動被回收掉了。
測試代碼如下:

public class JvmStackOOM {  
    private int stackLength = 1;  

    public void execute() {  
        try {  
            stackLeak();  
        } catch (Throwable e) {  
            System.out.println("stackLength : " + stackLength);  
            e.printStackTrace();  
        }  
    }  

    private void stackLeak() {  
        stackLength++;  
        stackLeak();  
    }  
}  

用一個遞歸不斷地對實例變量stackLength進行自增操作,當JVM在擴展棧時無法申請到足夠的空間,將產生StackOverflowError
可以使用Jvm 參數-Xss配置棧大小,例如:-Xss2M,棧內存越大,可的棧深度越大,在內存不變的情況下,jvm可創建的線程就越少,需要合理設置。

2.2 方法區內存分配:

類信息和運行時常量將會分配到此區域。
測試代碼如下:

public class JvmRuntimeConstantPoolOOM {  
    private int runtimeConstantCount = 1;  

    public void execute() {  
        try {  
            runtimeConstantLeak();  
        } catch (Throwable e) {  
            System.out.println("runtimeConstantCount : " + runtimeConstantCount);  
            e.printStackTrace();  
        }  
    }  

    private void runtimeConstantLeak() {  
        List<String> list = new ArrayList<String>();  
        while (true) {  
            list.add(String.valueOf(runtimeConstantCount++).intern());  
        }  
    }  
}  

使用String的intern()方法向方法區中灌入數據,當方法區內存不足時,拋出OutOfMemoryError: PermGen space,
也可以加載過多的類的方式,測試是否有OutOfMemoryError: PermGen space異常,如果有說明類信息也是存放在方法區中的可以
使用Jvm 參數-XX:PermSize和-XX:MaxPermSize配置棧大小,例如:-XX:PermSize=10M -XX:MaxPermSize=10M

2.3 堆內存分配:

所有對象實例及數組都會在堆上分配。
堆分爲新生代和老年代。新生代分爲3個區域:一個eden區,和兩個survivor區(互爲From、To,相對的),
新建對象時首先想eden區申請分配空間,如果空間夠,就直接進行分配,否則進行一次Minor GC(新生代垃圾回收)。
Minor GC後再次嘗試將對象放到eden區,如果空間仍然不夠,直接在老年代創建對象。
測試代碼如下:

public class JvmHeapOOM {  
    private int bojectCount = 1;  

    public void execute() {  
        try {  
            heapLeak();  
        } catch (Throwable e) {  
            System.out.println("bojectCount : " + bojectCount);  
            e.printStackTrace();  
        }  
    }  

    private void heapLeak() {  
        List<OOMObject> list = new ArrayList<OOMObject>();  
        while (true) {  
            list.add(new OOMObject());  
            bojectCount++;  
        }  
    }  

    private class OOMObject {  

    }  
} 

創建多個OOMObject對象放到List中,當堆內存不足時,產生OutOfMemoryError:Java Heap space
使用Jvm 參數-Xm -Xmx -Xmn -XX:SurvivorRatio配置堆,例如:-Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8

2.4 本地直接內存分配:

堆外內存,NIO相關操作將在此分配內存
使用Jvm 參數-XX:MaxDirectMemorySize配置,例如:-XX:MaxDirectMemorySize=10M

2.5 所有用到的JVM啓動參數:

-Xss2M 設置JVM棧內存大小
-Xms20M 設置堆內存初始值
-Xmx20M 設置堆內存最大值
-Xmn10M 設置堆內存中新生代大小
-XX:SurvivorRatio=8 設置堆內存中新生代Eden 和 Survivor 比例
-XX:PermSize=10M 設置方法區內存初始值
-XX:MaxPermSize=10M 設置方法區內存最大值
-XX:MaxDirectMemorySize=10M 設置堆內存中新生代大小

發佈了63 篇原創文章 · 獲贊 134 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章