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

《深刻理解Java虛擬機》讀後總結java

(一)Sun HotSpot JVM內存模型數組

(二)Sun HotSpot JVM內存分配jvm

(三)Sun HotSpot JVM內存監控ide


基於Sun HotSpot JVM測試

請先了解JVM內存模型在來看此篇文章優化

使用對JVM不一樣內存區域灌入數據,致使相關區域內存溢出,來驗證JVM內存分配spa


先看一個經典問題:code



1
2
3
4
5
6
7
8
9
10
11
12
13
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)");blog

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會進行「+」運算符號的優化,兩遍都是字符串常量會作相似於第二種的處理,若是「+」任意一邊是一個變量,就會作相似第一種的處理。


JVM棧和Native Method棧內存分配:

JAVA中八個基本類型數據,在運行時都是分配在棧中的。

測試代碼以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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


方法區內存分配:

類信息和運行時常量將會分配到此區域。

測試代碼以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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


堆內存分配:

全部對象實例及數組都會在堆上分配。

測試代碼以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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


本地直接內存分配:

堆外內存,NIO相關操做將在此分配內存

使用Jvm 參數-XX:MaxDirectMemorySize配置,例如:-XX:MaxDirectMemorySize=10M


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



本文出自 「AUB」 博客,請務必保留此出處http://aubdiy.blog.51cto.com/2978849/1207518

相關文章
相關標籤/搜索