JVM常用參數配置


本文主要內容:

  • Trace跟蹤參數
  • 堆的分配參數
  • 棧的分配參數
  • JVM其他參數

既然學習JVM,閱讀GC日志是處理Java虛擬機內存問題的基礎技能,它只是一些人為確定的規則,沒有太多技術含量。

既然如此,那么在IDE的控制台打印GC日志是必不可少的了。現在就告訴你怎么打印。

(1)如果你用的是Eclipse,打印GC日志的操作如下:

d32742cf-b002-4c55-a185-d4ccdc90a69c

bc5b8afb-9d1f-438b-9225-ee7fbbbe2454

在上圖的箭頭處加上-XX:+PrintGCDetails這句話。於是,運行程序后,GC日志就可以打印出來了:

25d80649-69f0-47b2-a3bb-418ba4457849

(2)如果你用的是IntelliJ IDEA,打印GC日志的操作如下:

94726055-e81f-45b8-8978-d1277c5acb17

f2c896da-404c-4415-98ef-5b582dec3528

在上圖的箭頭處加上-XX:+PrintGCDetails這句話。於是,運行程序后,GC日志就可以打印出來了:

6b1b4352-7172-4404-ac6c-b94c16036d73

當然了,光有-XX:+PrintGCDetails這一句參數肯定是不夠的,下面我們詳細介紹一下更多的參數配置。

 

一、Trace跟蹤參數:

1、打印GC的簡要信息:

-verbose:gc
-XX:+printGC

解釋:可以打印GC的簡要信息。比如:

[GC 4790K->374K(15872K), 0.0001606 secs]

[GC 4790K->374K(15872K), 0.0001474 secs]

[GC 4790K->374K(15872K), 0.0001563 secs]

[GC 4790K->374K(15872K), 0.0001682 secs]

上方日志的意思是說,GC之前,用了4M左右的內存,GC之后,用了374K內存,一共回收了將近4M。內存大小一共是16M左右。

 

2、打印GC的詳細信息:

-XX:+PrintGCDetails

解釋:打印GC詳細信息。

-XX:+PrintGCTimeStamps

解釋:打印CG發生的時間戳。

 

理解GC日志的含義:

例如下面這段日志:

[GC[DefNew: 4416K->0K(4928K), 0.0001897 secs] 4790K->374K(15872K), 0.0002232 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

上方日志的意思是說:這是一個新生代的GC。方括號內部的“4416K->0K(4928K)”含義是:“GC前該內存區域已使用容量->GC后該內存區域已使用容量(該內存區域總容量)”。而在方括號之外的“4790K->374K(15872K)”表示“GC前Java堆已使用容量->GC后Java堆已使用容量(Java堆總容量)”。

再往后看,“0.0001897 secs”表示該內存區域GC所占用的時間,單位是秒。

 

再比如下面這段GC日志:

1fe41f36-cc6b-4a8b-b48e-8cbe2e3a04af

上圖中,我們先看一下用紅框標注的“[0x27e80000, 0x28d80000, 0x28d80000)”的含義,它表示新生代在內存當中的位置:第一個參數是申請到的起始位置,第二個參數是申請到的終點位置,第三個參數表示最多能申請到的位置。上圖中的例子表示新生代申請到了15M的空間,而這個15M是等於:(eden space的12288K)+(from space的1536K)+(to space的1536K)

疑問:分配到的新生代有15M,但是可用的只有13824K,為什么會有這個差異呢?等我們在后面的文章中學習到了GC算法之后就明白了。

 

3、指定GC log的位置:

-Xloggc:log/gc.log

解釋:指定GC log的位置,以文件輸出。幫助開發人員分析問題。

805e8e33-1e3b-46c0-af9d-d68f4d38816f

  

-XX:+PrintHeapAtGC

解釋:每一次GC前和GC后,都打印堆信息。

例如:

1c6f3837-4b31-4ac2-a639-e79c92f80df5

上圖中,紅框部分正好是一次GC,紅框部分的前面是GC之前的日志,紅框部分的后面是GC之后的日志。

 

-XX:+TraceClassLoading

解釋:監控類的加載。

例如:

[Loaded java.lang.Object from shared objects file]

[Loaded java.io.Serializable from shared objects file]

[Loaded java.lang.Comparable from shared objects file]

[Loaded java.lang.CharSequence from shared objects file]

[Loaded java.lang.String from shared objects file]

[Loaded java.lang.reflect.GenericDeclaration from shared objects file]

[Loaded java.lang.reflect.Type from shared objects file]

 

-XX:+PrintClassHistogram

 

解釋:按下Ctrl+Break后,打印類的信息。

例如:

c8050739-0029-47cd-95bd-fbbd6289a5d1

 二、堆的分配參數:

  JVM 中最大堆大小有三方面限制:相關操作系統的數據模型(32-bt還是64-bit)限制;系統的可用虛擬內存限制;系統的可用物理內存限制。32位操作系統雖然尋址空間大小是4G(2^32),但具體操作系統會給一個限制(一般Windows系統有2GB內核空間,故用戶空間限制在1.5G~2G,Linux系統有1GB內核空間,故用戶空間是2G~3G);64為操作系統對內存無限制。所有線程共享數據區大小=新生代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m。所以java堆中增大年輕代后,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為java堆的3/8

1、-Xmx –Xms:指定java堆最大值(默認值是物理內存的1/4(<1GB))和初始java堆最小值(默認值是物理內存的1/64(<1GB))

  默認(MinHeapFreeRatio參數可以調整)空余堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制.,默認(MaxHeapFreeRatio參數可以調整)空余堆內存大於70%時,JVM會減少堆直到 -Xms的最小限制。開發過程中,通常會將 -Xms 與 -Xmx兩個參數的配置相同的值,其目的是為了能夠在java垃圾回收機制清理完堆區后不需要重新分隔計算堆區的大小而浪費資源。

注意:此處設置的是Java堆大小,也就是新生代大小 + 年老代大小

舉例、當參數設置為如下時:

-Xmx20m -Xms5m 

然后我們在程序中運行如下代碼:

System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M");     //系統的最大空間
System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系統的空閑空間
System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //當前可用的總空間

 運行效果:

79c1029d-58fe-47d9-aa2e-1c5ee7e741cd

保持參數不變,在程序中運行如下代碼:(分配1M空間給數組)

復制代碼
byte[] b = new byte[1 * 1024 * 1024];
System.out.println("分配了1M空間給數組");
System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系統的最大空間
System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系統的空閑空間
System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M");
復制代碼

運行效果:

14d260c9-28bf-4544-a36f-ee14a1d59623

注:Java會盡可能將total mem的值維持在最小堆。

保持參數不變,在程序中運行如下代碼:(分配10M空間給數組)

復制代碼
byte[] b = new byte[10 * 1024 * 1024];
System.out.println("分配了10M空間給數組");
System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系統的最大空間
System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系統的空閑空間
System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //當前可用的總空間
復制代碼

運行效果:

284e8036-8d70-46bc-aac1-99c9b3deb3ef

如上圖紅框所示:此時,total mem 為7M時已經不能滿足需求了,於是total mem漲成了16.5M。

 

保持參數不變,在程序中運行如下代碼:(進行一次GC的回收)

復制代碼
System.gc();
System.out.println("Xmx=" + Runtime.getRuntime().maxMemory() / 1024.0 / 1024 + "M"); //系統的最大空間
System.out.println("free mem=" + Runtime.getRuntime().freeMemory() / 1024.0 / 1024 + "M"); //系統的空閑空間
System.out.println("total mem=" + Runtime.getRuntime().totalMemory() / 1024.0 / 1024 + "M"); //當前可用的總空間 
復制代碼

運行效果:

e419c020-0da3-4046-9b7f-f542ee14a780

 

2、-Xmn、-XX:NewRatio、-XX:SurvivorRatio、-XXNewSize、-XX:MaxNewSize:

  • -Xmn

    設置新生代大小,大小是:eden+ 2 survivor space

  所有共享數據區大小=年輕代大小 + 年老代大小 + 持久代大小。一般永久代大小固定,所以增大年輕代后,將會減小年老代大小,此值對系統性能影響較大,Sun官方推薦配置為整個java堆的3/8

  • -XX:NewRatio

    新生代(eden+2*Survivor)和老年代(不包含永久區)的比值

        例如:-XX:NewRatio=4,表示新生代:老年代=1:4,即新生代占整個堆的1/5。在Xms=Xmx並且設置了Xmn的情況下,該參數不需要進行設置。

  • -XX:SurvivorRatio(幸存代)

    設置兩個Survivor區和eden的比值

        例如:8,表示兩個Survivor:eden=2:8,即一個Survivor占年輕代的1/10

  • -XX:NewSize

    設置年輕代大小

  • -XX:MaxNewSize

    設置年輕代最大值

現在運行如下這段代碼:

復制代碼
public class JavaTest {
    public static void main(String[] args) {
        byte[] b = null;
        for (int i = 0; i < 10; i++)
            b = new byte[1 * 1024 * 1024];
    }
}
復制代碼

我們通過設置不同的jvm參數,來看一下GC日志的區別。

 

(1)當參數設置為如下時:(設置新生代為1M,很小)

-Xmx20m -Xms20m -Xmn1m -XX:+PrintGCDetails 

運行效果:

4f0b24b4-cc74-4fd6-af15-b30a784d351b

總結:

  沒有觸發GC

    由於新生代的內存比較小,所以全部分配在老年代。

 

(2)當參數設置為如下時:(設置新生代為15M,足夠大)

-Xmx20m -Xms20m -Xmn15m -XX:+PrintGCDetails

運行效果:

2cb6145f-8c1b-4269-bcfa-31912d2f0d41

上圖顯示:

沒有觸發GC

全部分配在eden(藍框所示)

老年代沒有使用(紅框所示)

 

(3)當參數設置為如下時:(設置新生代為7M,不大不小)

-Xmx20m -Xms20m –Xmn7m -XX:+PrintGCDetails

運行效果:

0e0cc65d-e291-477a-ba7f-7d433f1085cc

總結:

  進行了2次新生代GC

  s0 s1 太小,需要老年代擔保

 

(4)當參數設置為如下時:(設置新生代為7M,不大不小;同時,增加幸存代大小)

-Xmx20m -Xms20m -Xmn7m -XX:SurvivorRatio=2 -XX:+PrintGCDetails

運行效果:

35eb96d6-9251-45e5-8120-05b82210df06

總結:

    進行了至少3次新生代GC

    s0 s1 增大

 

(5)當參數設置為如下時:

-Xmx20m -Xms20m -XX:NewRatio=1

-XX:SurvivorRatio=2 -XX:+PrintGCDetails 

運行效果:

c85f7057-1842-4d11-bc28-fc766e5681f8

 

 

(6)當參數設置為如下時: 和上面的(5)相比,適當減小幸存代大小,這樣的話,能夠減少GC的次數

-Xmx20m -Xms20m -XX:NewRatio=1

-XX:SurvivorRatio=3 -XX:+PrintGCDetails

fd3322ec-a853-49aa-86fa-81d8b3a02f8c

 

3、-XX:+HeapDumpOnOutOfMemoryError、-XX:+HeapDumpPath

  • -XX:+HeapDumpOnOutOfMemoryError

    OOM時導出堆到文件

      根據這個文件,我們可以看到系統dump時發生了什么。

  • -XX:+HeapDumpPath

    導出OOM的路徑

例如我們設置如下的參數:

-Xmx20m -Xms5m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=d:/a.dump

上方意思是說,現在給堆內存最多分配20M的空間。如果發生了OOM異常,那就把dump信息導出到d:/a.dump文件中。

然后,我們執行如下代碼:

Vector v = new Vector();
for (int i = 0; i < 25; i++)
  v.add(new byte[1 * 1024 * 1024]);

上方代碼中,需要利用25M的空間,很顯然會發生OOM異常。現在我們運行程序,控制台打印如下:

3320aba5-2aa6-42bc-b656-57bbc5d8ec41

現在我們去D盤看一下dump文件:

8782a0ae-62fb-43a8-a5a6-1c5691e7fa59

上圖顯示,一般來說,這個文件的大小和最大堆的大小保持一致。

我們可以用VisualVM打開這個dump文件。

注:關於VisualVM的使用,可以參考下面這篇博客:

使用 VisualVM 進行性能分析及調優:http://www.ibm.com/developerworks/cn/java/j-lo-visualvm/

或者使用Java自帶的Java VisualVM工具也行:

f9158d50-95d0-4732-942c-e872181fa530

f69bd0d2-a355-4a93-81c1-c3e71bce7509

上圖中就是dump出來的文件,文件中可以看到,一共有19個byte已經被分配了。 

 

4、-XX:OnOutOfMemoryError:

  • -XX:OnOutOfMemoryError

    在OOM時,執行一個腳本。

      可以在OOM時,發送郵件,甚至是重啟程序。

例如我們設置如下的參數:

-XX:OnOutOfMemoryError=D:/tools/jdk1.7_40/bin/printstack.bat %p //p代表的是當前進程的pid 

上方參數的意思是說,執行printstack.bat腳本,而這個腳本做的事情是:D:/tools/jdk1.7_40/bin/jstack -F %1 > D:/a.txt,即當程序OOM時,在D:/a.txt中將會生成線程的dump。

5、堆的分配參數總結:

  • 根據實際事情調整新生代和幸存代的大小
  • 官方推薦新生代占java堆的3/8
  • 幸存代占新生代的1/10
  • 在OOM時,記得Dump出堆,確保可以排查現場問題

6、永久區分配參數:

  • -XX:PermSize  -XX:MaxPermSize

    設置永久區的初始空間(默認為物理內存的1/64)和最大空間(默認為物理內存的1/4)。也就是說,jvm啟動時,永久區一開始就占用了PermSize大小的空間,如果空間還不夠,可以繼續擴展,但是不能超過MaxPermSize,否則會OOM。

    他們表示,一個系統可以容納多少個類型

代碼舉例:

我們知道,使用CGLIB等庫的時候,可能會產生大量的類,這些類,有可能撐爆永久區導致OOM。於是,我們運行下面這段代碼:

for(int i=0;i<100000;i++){
  CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());
}

上面這段代碼會在永久區不斷地產生新的類。於是,運行效果如下:

fd7bcefb-d6d5-4fe0-8d77-9cddae2733fc

總結:

  如果堆空間沒有用完也拋出了OOM,有可能是永久區導致的

    堆空間實際占用非常少,但是永久區溢出 一樣拋出OOM。

三、棧的分配參數:

1、-Xss:

設置每個線程棧空間的大小。JDK5.0以后每個線程堆棧大小為1M,以前每個線程堆棧大小為256K。在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右

  決定了函數調用的深度

  每個線程都有獨立的棧空間

  局部變量、參數 分配在棧上

注:棧空間是每個線程私有的區域。棧里面的主要內容是棧幀,而棧幀存放的是局部變量表,局部變量表的內容是:局部變量、參數。

我們來看下面這段代碼:(沒有出口的遞歸調用)

復制代碼
public class TestStackDeep {
    private static int count = 0;
public static void recursion(long a, long b, long c) { long e = 1, f = 2, g = 3, h = 4, i = 5, k = 6, q = 7, x = 8, y = 9, z = 10; count++; recursion(a, b, c); }
public static void main(String args[]) { try { recursion(0L, 0L, 0L); } catch (Throwable e) { System.out.println("deep of calling = " + count); e.printStackTrace(); } } }
復制代碼

上方這段代碼是沒有出口的遞歸調用,肯定會出現OOM的。

如果設置棧大小為128k:

-Xss128K 

運行效果如下:(方法被調用了294次)

5c2b2060-e54a-4e7c-9a30-81567204d55b

如果設置棧大小為256k:(方法被調用748次)

7d6be7d6-b646-42bf-9357-1a3bccbb7a49

意味着函數調用的次數太深,像這種遞歸調用就是個典型的例子。

2、-XXThreadStackSize:

設置線程棧的大小(0 means use default stack size)

一、JVM其他參數:

1、-XXThreadStackSize:

設置內存頁的大小,不可設置過大,會影響Perm的大小

2、-XX:+UseFastAccessorMethods:

設置原始類型的快速優化

3、-XX:+DisableExplicitGC:

設置關閉System.gc()(這個參數需要嚴格的測試)

4、-XX:MaxTenuringThreshold    

設置垃圾最大年齡。如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代. 對於年老代比較多的應用,可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概率。該參數只有在串行GC時才有效.

5、-XX:+AggressiveOpts

加快編譯                 

6、-XX:+UseBiasedLocking  

鎖機制的性能改善                 

7、-Xnoclassgc    

禁用垃圾回收                 

8、-XX:SoftRefLRUPolicyMSPerMB      

設置每兆堆空閑空間中SoftReference的存活時間,默認值是1s 。(softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap)

9、-XX:PretenureSizeThreshold   

設置對象超過多大時直接在舊生代分配,默認值是0。

10、-XX:TLABWasteTargetPercent        

設置TLAB占eden區的百分比,默認值是1% 。  

11、-XX:+CollectGen0First     

設置FullGC時是否先YGC,默認值是false。

 

 

 

 

 

 

 

原文出處:http://www.cnblogs.com/smyhvae/p/4736162.html

參考文獻:http://www.cnblogs.com/redcreen/archive/2011/05/04/2037057.html(含JVM詳細參數說明)

    http://www.cnblogs.com/edwardlauxh/archive/2010/04/25/1918603.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM