JVM 配置常用參數
- 堆參數;
- 回收器參數;
- 項目中常用配置;
- 常用組合;
堆參數
回收器參數
如上表所示,目前主要有串行、並行和並發三種,對於大內存的應用而言,串行的性能太低,因此使用到的主要是並行和並發兩種。並行和並發 GC 的策略通過 UseParallelGC
和 UseConcMarkSweepGC
來指定,還有一些細節的配置參數用來配置策略的執行方式。例如:XX:ParallelGCThreads
, XX:CMSInitiatingOccupancyFraction
等。 通常:Young 區對象回收只可選擇並行(耗時間),Old 區選擇並發(耗 CPU)。
項目中常用配置
備注:在Java8中永久代的參數
-XX:PermSize
和-XX:MaxPermSize
已經失效。
常用組合
一、gc日志收集
打印Gc日志的參數
打印gc詳細信息
-XX:+PringGCDetails
帶有距離JVM開始運行的時間戳
-XX:+PrintGCTimeStamps
帶有日歷時間戳
--XX:+PringGCDateStamps
指定gc日志存放文件(不指定則控制台打印)
-Xloggc:
針對高延遲問題調優HotSpot VM時,下面兩個命令行選項特別有用,通過它們可以獲得應用程序由於執行VM安全操作而阻塞的時間以及兩個安全點操作之間應用程序運行的時間。
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCApplicationConcurrentTime
何謂安全操作:安全操作使JVM進入到一種狀態:所有的java應用線程都被阻塞、執行本地代碼的線程都被禁止返回VM執行Java代碼。安全操作常用於虛擬機需要進行內部操作時,此時所有的Java線程都被顯式地置於阻塞狀態且不能修改Java堆的情況。
設置參數
Tomcat
$CATALINA_HOME/bin/setenv.sh文件
加入:
#opts for gc log
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDetails"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCTimeStamps"
export CATALINA_OPTS="$CATALINA_OPTS -XX:+PrintGCDateStamps"
export CATALINA_OPTS="$CATALINA_OPTS -Xloggc:/gc_logs/tomcat.gc.log"
當然,loggc的文件能夠自給創建,目錄得先創建好。
二、GC日志可視化分析工具GCeasy和GCViewer
GCeasy介紹
官網地址:https://gceasy.io/,GCeasy是一款在線的GC日志分析器,可以通過GC日志分析進行內存泄露檢測、GC暫停原因分析、JVM配置建議優化等功能,而且是可以免費使用的(有一些服務是收費的)。
上節《GC日志介紹》我們介紹了各個垃圾收集器的GC日志,我們打開GCeasy的官網,上傳我們的GC日志(我這里用的Parallel收集器),點擊Analyze進行分析即可得到報告,得到的報告可以進行下載。由於報告內容比較多,我就撿幾個主要的截個圖簡單說明一下,其他的可以自己看一下,而且可以自己試試不同的垃圾收集器的GC日志的分析報告有什么區別。
JVM的各個分代區域分配的內存及使用峰值的內存
關鍵性能指標:吞吐量及GC暫停平均時間、最大時間、各個時間段的比例。
GC發生的原因、次數、時間等
GCViewer介紹
上面介紹了一款在線的GC日志分析器,下面介紹一個離線版的GCViewer,其最新版本為1.36,我用的就是這個版本,需要jdk1.8才可以使用,Github地址為https://github.com/chewiebug/GCViewer,下載下來之后執行 mvn clean install -Dmaven.test.skip=true 命令進行編譯,編譯完成后在target目錄下會看到jar包,雙擊打開即可。
打開之后,點擊File->Open File打開我們的GC日志,可以看到如下圖,圖標是可以放大縮小的,主要內容就是紅線圈住的部分,里面的內容跟上面的GCeasy的比較類似,具體的可以看下GitHub中的描述。
三、系統CPU經常100%,如何定位
1、首先要確認哪個進程占用CPU高,可以使用top命令

2、找到之后可以繼續執行top -Hp PID命令查詢出占用最大的線程

3、執行jstack命令生成線程快照信息:
jstack -l 進程PID >jstack.log
1
輸出之后,我們找到上面占用CPU最高的一個線程pid=11566,將其轉換為16進制,得到的結果是2d2e,然后進入生成的jstack.log文件找到這個線程可以查看線程信息。

4、上面就可以定位到了線程調用的方法了,接下來就可以去分析對應的代碼尋找問題了
GC 調優目的
將轉移到老年代的對象數量降低到最小; 減少 GC 的執行時間。
GC 調優策略
**策略 1:**將新對象預留在新生代,由於 Full GC 的成本遠高於 Minor GC,因此盡可能將對象分配在新生代是明智的做法,實際項目中根據 GC 日志分析新生代空間大小分配是否合理,適當通過“-Xmn”命令調節新生代大小,最大限度降低新對象直接進入老年代的情況。
**策略 2:**大對象進入老年代,雖然大部分情況下,將對象分配在新生代是合理的。但是對於大對象這種做法卻值得商榷,大對象如果首次在新生代分配可能會出現空間不足導致很多年齡不夠的小對象被分配的老年代,破壞新生代的對象結構,可能會出現頻繁的 full gc。因此,對於大對象,可以設置直接進入老年代(當然短命的大對象對於垃圾回收來說簡直就是噩夢)。-XX:PretenureSizeThreshold
可以設置直接進入老年代的對象大小。
**策略 3:**合理設置進入老年代對象的年齡,-XX:MaxTenuringThreshold
設置對象進入老年代的年齡大小,減少老年代的內存占用,降低 full gc 發生的頻率。
**策略 4:**設置穩定的堆大小,堆大小設置有兩個參數:-Xms
初始化堆大小,-Xmx
最大堆大小。
**策略5:**注意: 如果滿足下面的指標,則一般不需要進行 GC 優化:
MinorGC 執行時間不到50ms; Minor GC 執行不頻繁,約10秒一次; Full GC 執行時間不到1s; Full GC 執行頻率不算頻繁,不低於10分鍾1次。