引言:JVM提供了諸多的參數進行JVM各個方面內存大小的設置,爲Java應用進行優化提供了諸多的工具,本文將會詳細分析各個參數的功能與使用。
1. 內存大小-Xmx/-Xms
使用示例: -Xmx20m -Xms5m
說明: 當下Java應用最大可用內存爲20M, 最小內存爲5M
代碼測試1:
public class JVMTest {
public static void main(String[] args) {
System.out.print("Xmx=");
System.out.println(Runtime.getRuntime().maxMemory()/1024.0/1024+"M");
System.out.print("free mem=");
System.out.println(Runtime.getRuntime().freeMemory()/1024.0/1024+"M");
System.out.print("total mem=");
System.out.println(Runtime.getRuntime().totalMemory()/1024.0/1024+"M");
}
}
程序執行結果:
大家可以發現,這裏打印出來的Xmx值和設置的值之間是由差異的,total Memory和最大的內存之間還是存在一定差異的,就是說JVM一般會盡量保持內存在一個儘可能底的層面,而非貪婪做法按照最大的內存來進行分配。
在測試代碼中新增如下語句,申請內存分配:
byte[] b=new byte[4*1024*1024];
System.out.println("分配了1M空間給數組");
程序運行結果:
在申請分配了4m內存空間之後,total memory上升了,同時可用的內存也上升了,可以發現其實JVM在分配內存過程中是動態的, 按需來分配的。
2. 各個代系之間的大小設置
-Xmn新生代的設置
-XX:NewRatio新生代和年老代的比例; 4表示年輕代:年老代=1:4, 即年輕代佔堆的1/5.
-XX:SurvivorRatio新生代內部Survivor和Eden區域的比例; 示例: 8表示兩個Survivor:Eden=2:8, 即一個Survivor佔1/10的新生代空間
代碼示例:
JVM的設置爲: -Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails
byte[] b=null;
for(int i=0;i<10;i++)
b=new byte[1*1024*1024];
結果輸出:
[GC (Allocation Failure) [PSYoungGen: 512K->488K(1024K)] 512K->488K(19968K), 0.0014737 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Xmx=19.5M
free mem=8.774787902832031M
total mem=19.5M
Heap
PSYoungGen total 1024K, used 747K [0x00000000ffe80000, 0x0000000100000000, 0x0000000100000000)
eden space 512K, 50% used [0x00000000ffe80000,0x00000000ffec0fa8,0x00000000fff00000)
from space 512K, 95% used [0x00000000fff00000,0x00000000fff7a020,0x00000000fff80000)
to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
ParOldGen total 18944K, used 10240K [0x00000000fec00000, 0x00000000ffe80000, 0x00000000ffe80000)
object space 18944K, 54% used [0x00000000fec00000,0x00000000ff6000a0,0x00000000ffe80000)
Metaspace used 2546K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 279K, capacity 386K, committed 512K, reserved 1048576K
Java HotSpot(TM) 64-Bit Server VM warning: NewSize (1536k) is greater than the MaxNewSize (1024k). A new max generation size of 1536k will be used.
結果分析:
由於內存申請大小爲1M, 故年輕代無法滿足需求,所以10m的內存分配都放到了年老代。這裏發生了一次GC,一個內存分配失敗,在年輕代中。
關注一下Hotspot的Warning信息,這個提示由於年輕帶比較小,故其將其設置了一個NewSize, 較大的值1536k,而非JVM參數中設置的1M大小。
代碼示例2:
JVM參數設置: -Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails
GC輸出結果:
Xmx=18.5M
free mem=7.484504699707031M
total mem=18.5M
Heap
PSYoungGen total 13824K, used 11525K [0x00000000ff100000, 0x0000000100000000, 0x0000000100000000)
eden space 12288K, 93% used [0x00000000ff100000,0x00000000ffc417f8,0x00000000ffd00000)
from space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
to space 1536K, 0% used [0x00000000ffd00000,0x00000000ffd00000,0x00000000ffe80000)
ParOldGen total 5120K, used 0K [0x00000000fec00000, 0x00000000ff100000, 0x00000000ff100000)
object space 5120K, 0% used [0x00000000fec00000,0x00000000fec00000,0x00000000ff100000)
Metaspace used 2542K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 279K, capacity 386K, committed 512K, reserved 1048576K
結果分析: 沒有發生分配失敗和GC回收。 10M的分配都進入了Eden區域。 年老代中爲0, Survivior區域爲0.
代碼示例3:
JVM參數設置:-Xmx20m -Xms20m -Xmn7m -XX:+PrintGCDetails
GC結果輸出:
[GC (Allocation Failure) [PSYoungGen: 5962K->488K(6656K)] 5962K->1592K(19968K), 0.0013317 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Xmx=19.5M
free mem=12.827682495117188M
total mem=19.5M
Heap
PSYoungGen total 6656K, used 5790K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 6144K, 86% used [0x00000000ff900000,0x00000000ffe2d8d8,0x00000000fff00000)
from space 512K, 95% used [0x00000000fff00000,0x00000000fff7a020,0x00000000fff80000)
to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
ParOldGen total 13312K, used 1104K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 8% used [0x00000000fec00000,0x00000000fed14010,0x00000000ff900000)
Metaspace used 2543K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 279K, capacity 386K, committed 512K, reserved 1048576K
結果分析:
發生了GC回收,年輕代中回收了將近5M內存,整體佔用內存是19M,包括(年輕代和年老代), 用時0.001秒
代碼示例4:
JVM參數設置: -Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails
結果輸出:
[GC (Allocation Failure) [PSYoungGen: 3882K->1512K(5632K)] 3882K->1600K(18944K), 0.0013471 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 4664K->1520K(5632K)] 4752K->1656K(18944K), 0.0012496 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 4663K->1496K(5632K)] 4799K->1632K(18944K), 0.0061350 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
Xmx=18.5M
free mem=15.833610534667969M
total mem=18.5M
Heap
PSYoungGen total 5632K, used 2676K [0x00000000ff900000, 0x0000000100000000, 0x0000000100000000)
eden space 4096K, 28% used [0x00000000ff900000,0x00000000ffa27268,0x00000000ffd00000)
from space 1536K, 97% used [0x00000000ffd00000,0x00000000ffe76040,0x00000000ffe80000)
to space 1536K, 0% used [0x00000000ffe80000,0x00000000ffe80000,0x0000000100000000)
ParOldGen total 13312K, used 136K [0x00000000fec00000, 0x00000000ff900000, 0x00000000ff900000)
object space 13312K, 1% used [0x00000000fec00000,0x00000000fec22000,0x00000000ff900000)
Metaspace used 2542K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 279K, capacity 386K, committed 512K, reserved 1048576K
結果分析:
這裏SurvivorRatio=2, 表示 2Survior: Eden = 2;2, 這裏計算的值不是完全按照1:1來設置的,具體原因未知, 3:4 的結果。 但是設置到10m的時候,就是2:2來走的,這裏的可能原因是小數點的值取捨。
發了3次GC,都是發生在新生代。
代碼示例5:
JVM參數: -Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=2 -XX:+PrintGCDetails
結果輸出:
[GC (Allocation Failure) [PSYoungGen: 4932K->1608K(7680K)] 4932K->1616K(17920K), 0.0750661 secs] [Times: user=0.08 sys=0.00, real=0.08 secs]
[GC (Allocation Failure) [PSYoungGen: 5804K->1576K(7680K)] 5812K->1584K(17920K), 0.0223095 secs] [Times: user=0.03 sys=0.00, real=0.02 secs]
Xmx=17.5M
free mem=13.863265991210938M
total mem=17.5M
Heap
PSYoungGen total 7680K, used 3818K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 5120K, 43% used [0x00000000ff600000,0x00000000ff830af0,0x00000000ffb00000)
from space 2560K, 61% used [0x00000000ffd80000,0x00000000fff0a030,0x0000000100000000)
to space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000)
ParOldGen total 10240K, used 8K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 0% used [0x00000000fec00000,0x00000000fec02000,0x00000000ff600000)
Metaspace used 2542K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 279K, capacity 386K, committed 512K, reserved 1048576K
結果分析:
NewRatio爲1, 則新生代和年老代的比例是1:1, 都爲10M。 SurvivorRatio爲2, 則2個survior:eden = 2:2, 平均分配, 5m, 2.5m, 2.5m.
發生了兩次GC操作。分別回收了大約3m和4m的內存空間。
代碼示例6:
JVM參數:-Xmx20m -Xms20m -XX:NewRatio=1 -XX:SurvivorRatio=3 -XX:+PrintGCDetails
結果輸出:
[GC (Allocation Failure) [PSYoungGen: 5962K->1656K(8192K)] 5962K->1664K(18432K), 0.0025830 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Xmx=18.0M
free mem=11.257377624511719M
total mem=18.0M
Heap
PSYoungGen total 8192K, used 6958K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)
eden space 6144K, 86% used [0x00000000ff600000,0x00000000ffb2d8e0,0x00000000ffc00000)
from space 2048K, 80% used [0x00000000ffc00000,0x00000000ffd9e020,0x00000000ffe00000)
to space 2048K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x0000000100000000)
ParOldGen total 10240K, used 8K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)
object space 10240K, 0% used [0x00000000fec00000,0x00000000fec02000,0x00000000ff600000)
Metaspace used 2542K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 279K, capacity 386K, committed 512K, reserved 1048576K
結果分析:
出現了一次內存分配失敗,年輕代發生了一次GC回收,回收了將近4M空間。這次修改SurviorRatio的比例,將Eden區域放大,由於其分配的空間爲1m左右,則會降低GC的發生次數。
3. 堆的分配參數設置
-XX:+HeapDumpOnOutOfMemoryError OOM之時導出堆鏡像到文件
-XX:+HeapDumpPath 導出OOM文件的路徑設置
-XX:OnOutOfMemoryError 在OOM時,執行一個腳本
示例如下:-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p 在發生OOM之時,發送郵件或者重啓應用等動作。
代碼示例:
import java.util.ArrayList;
import java.util.List
public class OOMTest {
public static void main(String[] args) {
List<Object> refs = new ArrayList<Object>();
for (int i=0; i<25; i++) {
refs.add(new byte[1*1024*1024]);
}
}
}
JVM參數設置: -XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpPath=d:\oom.dump
結果輸出:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to d:\oom.dump ...
Heap dump file created [14814170 bytes in 0.022 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at org.homework.test.jvm.jvmopt.OOMTest.main(OOMTest.java:12)
Dump文件的位置在D盤的oom.dump,文件大小爲14m,一般這類文件的打開需要使用visualvm之類來進行。
打開JDK的安裝路徑,在bin目錄下即可發現visualvm的可程序程序:
基於visualvm打開dump文件,發現問題所在原因,是由於byte申請佔用太多的空間,達到98%的內容。
3. 總結
一般情況下,需要根據實際的情況而定來決定其比例和大小;記得在生產應用記得配置OOM的設置,確保在OOM情況下,可以將當時的堆文件鏡像轉儲到本地文件。或者可以配置觸發事件,確保發送OOM,可以通知相關方及時採取措施進行處理。
官方推薦新生代佔堆的3/8,倖存代佔新生代的1/10。