天天看點

Java虛拟機詳解03----常用JVM配置參數

本文主要内容:

trace跟蹤參數

堆的配置設定參數

棧的配置設定參數

零、在ide的背景列印gc日志:

既然學習jvm,閱讀gc日志是處理java虛拟機記憶體問題的基礎技能,它隻是一些人為确定的規則,沒有太多技術含量。

既然如此,那麼在ide的控制台列印gc日志是必不可少的了。現在就告訴你怎麼列印。

(1)如果你用的是eclipse,列印gc日志的操作如下:

Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數

在上圖的箭頭處加上-xx:+printgcdetails這句話。于是,運作程式後,gc日志就可以列印出來了:

Java虛拟機詳解03----常用JVM配置參數

(2)如果你用的是intellij idea,列印gc日志的操作如下:

Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數

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

一、trace跟蹤參數:

1、列印gc的簡要資訊:

解釋:可以列印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的詳細資訊:

解釋:列印gc詳細資訊。

解釋:列印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日志:

Java虛拟機詳解03----常用JVM配置參數

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

疑問:配置設定到的新生代有15m,但是可用的隻有13824k,為什麼會有這個差異呢?等我們在後面的文章中學習到了gc算法之後就明白了。

3、指定gc log的位置:

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

Java虛拟機詳解03----常用JVM配置參數

解釋:每一次gc前和gc後,都列印堆資訊。

例如:

Java虛拟機詳解03----常用JVM配置參數

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

解釋:監控類的加載。

[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]

解釋:按下ctrl+break後,列印類的資訊。

Java虛拟機詳解03----常用JVM配置參數

二、堆的配置設定參數:

1、-xmx –xms:指定最大堆和最小堆

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

然後我們在程式中運作如下代碼:

 運作效果:

Java虛拟機詳解03----常用JVM配置參數

保持參數不變,在程式中運作如下代碼:(配置設定1m空間給數組)

Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數

運作效果:

Java虛拟機詳解03----常用JVM配置參數

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

保持參數不變,在程式中運作如下代碼:(配置設定10m空間給數組)

Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數

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

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

Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數

問題1: -xmx(最大堆空間)和 –xms(最小堆空間)應該保持一個什麼關系,可以讓系統的性能盡可能的好呢?

問題2:如果你要做一個java的桌面産品,需要綁定jre,但是jre又很大,你如何做一下jre的瘦身呢?

2、-xmn、-xx:newratio、-xx:survivorratio:

-xmn

    設定新生代大小

-xx:newratio

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

        例如:4,表示新生代:老年代=1:4,即新生代占整個堆的1/5

-xx:survivorratio(幸存代)

    設定兩個survivor區和eden的比值

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

現在運作如下這段代碼:

Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數

我們通過設定不同的jvm參數,來看一下gc日志的差別。

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

Java虛拟機詳解03----常用JVM配置參數

總結:

  沒有觸發gc

    由于新生代的記憶體比較小,是以全部配置設定在老年代。

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

Java虛拟機詳解03----常用JVM配置參數

上圖顯示:

沒有觸發gc 全部配置設定在eden(藍框所示) 老年代沒有使用(紅框所示)

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

Java虛拟機詳解03----常用JVM配置參數

  進行了2次新生代gc

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

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

Java虛拟機詳解03----常用JVM配置參數

    進行了至少3次新生代gc

    s0 s1 增大

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

Java虛拟機詳解03----常用JVM配置參數

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

Java虛拟機詳解03----常用JVM配置參數

3、-xx:+heapdumponoutofmemoryerror、-xx:+heapdumppath

-xx:+heapdumponoutofmemoryerror

    oom時導出堆到檔案

      根據這個檔案,我們可以看到系統dump時發生了什麼。

-xx:+heapdumppath

    導出oom的路徑

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

上方意思是說,現在給堆記憶體最多配置設定20m的空間。如果發生了oom異常,那就把dump資訊導出到d:/a.dump檔案中。

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

上方代碼中,需要利用25m的空間,很顯然會發生oom異常。現在我們運作程式,控制台列印如下:

Java虛拟機詳解03----常用JVM配置參數

現在我們去d盤看一下dump檔案:

Java虛拟機詳解03----常用JVM配置參數

上圖顯示,一般來說,這個檔案的大小和最大堆的大小保持一緻。

我們可以用visualvm打開這個dump檔案。

注:關于visualvm的使用,可以參考下面這篇部落格:

或者使用java自帶的java visualvm工具也行:

Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數

上圖中就是dump出來的檔案,檔案中可以看到,一共有19個byte已經被配置設定了。 

4、-xx:onoutofmemoryerror:

-xx:onoutofmemoryerror

    在oom時,執行一個腳本。

      可以在oom時,發送郵件,甚至是重新開機程式。

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

5、堆的配置設定參數總結:

根據實際事情調整新生代和幸存代的大小

官方推薦新生代占堆的3/8

幸存代占新生代的1/10

在oom時,記得dump出堆,確定可以排查現場問題

6、永久區配置設定參數:

-xx:permsize  -xx:maxpermsize

    設定永久區的初始空間和最大空間。也就是說,jvm啟動時,永久區一開始就占用了permsize大小的空間,如果空間還不夠,可以繼續擴充,但是不能超過maxpermsize,否則會oom。

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

代碼舉例:

我們知道,使用cglib等庫的時候,可能會産生大量的類,這些類,有可能撐爆永久區導緻oom。于是,我們運作下面這段代碼:

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

Java虛拟機詳解03----常用JVM配置參數

  如果堆空間沒有用完也抛出了oom,有可能是永久區導緻的。

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

三、棧的配置設定參數:

1、xss:

設定棧空間的大小。通常隻有幾百k   決定了函數調用的深度   每個線程都有獨立的棧空間   局部變量、參數 配置設定在棧上

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

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

Java虛拟機詳解03----常用JVM配置參數
Java虛拟機詳解03----常用JVM配置參數

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

如果設定棧大小為128k:

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

Java虛拟機詳解03----常用JVM配置參數

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

Java虛拟機詳解03----常用JVM配置參數

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

我們在本文中介紹了jvm的一些最基本的參數,還有很多參數(如gc參數等)将在後續的系列文章中進行介紹。我們将在接下來的文章中介紹gc算法。