JVM內存設置參數分析

引言: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。

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