JVM經常使用參數配置

本文主要內容:html

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

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

既然如此,那麼在IDE的控制檯打印GC日誌是必不可少的了。如今就告訴你怎麼打印。算法

(1)若是你用的是Eclipse,打印GC日誌的操做以下:數組

d32742cf-b002-4c55-a185-d4ccdc90a69c

bc5b8afb-9d1f-438b-9225-ee7fbbbe2454

在上圖的箭頭處加上-XX:+PrintGCDetails這句話。因而,運行程序後,GC日誌就能夠打印出來了:jvm

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這一句參數確定是不夠的,下面咱們詳細介紹一下更多的參數配置。性能

 

1、Trace跟蹤參數:

一、打印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左右。

 

二、打印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算法以後就明白了。

 

三、指定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

 2、堆的分配參數:

  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

一、-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

 

二、-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

 

三、-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已經被分配了。 

 

四、-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。

五、堆的分配參數總結:

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

六、永久區分配參數:

  • -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。

3、棧的分配參數:

一、-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

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

二、-XXThreadStackSize:

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

1、JVM其餘參數:

一、-XXThreadStackSize:

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

二、-XX:+UseFastAccessorMethods:

設置原始類型的快速優化

三、-XX:+DisableExplicitGC:

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

四、-XX:MaxTenuringThreshold    

設置垃圾最大年齡。若是設置爲0的話,則年輕代對象不通過Survivor區,直接進入年老代. 對於年老代比較多的應用,能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在Survivor區進行屢次複製,這樣能夠增長對象再年輕代的存活時間,增長在年輕代即被回收的機率。該參數只有在串行GC時纔有效.

五、-XX:+AggressiveOpts

加快編譯                 

六、-XX:+UseBiasedLocking  

鎖機制的性能改善                 

七、-Xnoclassgc    

禁用垃圾回收                 

八、-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)

九、-XX:PretenureSizeThreshold   

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

十、-XX:TLABWasteTargetPercent        

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

十一、-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

相關文章
相關標籤/搜索