JVM GC調(diào)優(yōu)入門
這篇文章會介紹幾個常用的調(diào)優(yōu)參數(shù),再通過兩個案例介紹如何進行JVM GC調(diào)優(yōu)。閱讀這篇文章的前提是假設大家已經(jīng)對JVM內(nèi)存模型、JVM的垃圾回收算法益楼、JVM的垃圾回收器都比較熟悉了。
JVM調(diào)優(yōu)是必須的嗎点晴?
閱讀下面的內(nèi)容之前首先拋出這個問題:GC調(diào)優(yōu)對于java服務是必須的嗎感凤?實際上,我感覺80%的java的程序員在實際工作中都沒有碰到過GC調(diào)優(yōu)吧粒督,這是因為多數(shù)的Java應用不需要在服務器上進行GC優(yōu)化俊扭,多數(shù)導致GC問題的Java應用,都不是因為我們參數(shù)設置錯誤坠陈,而是代碼問題萨惑,需要記住一點:GC調(diào)優(yōu)是最后要做的工作。
GC調(diào)優(yōu)的目的可以總結(jié)為下面兩點:
- 減少對象晉升到老年代的數(shù)量
- 減少FullGC的執(zhí)行時間
減少對象晉升到老年代的數(shù)量
分代垃圾回收是Oracle JVM中回收思想仇矾。 我們知道在Eden區(qū)創(chuàng)建的對象庸蔼,在from Survivor 復制到to Survivor區(qū)之后,達到一定年齡就進入了老年代贮匕。有些對象因為比較大就直接進入了老年代姐仅。在老年代的GC時間相比于年輕代時間更長。因此刻盐,減少對象進入老年代可以降低Full GC的頻率
減少FullGC的執(zhí)行時間
Full GC的時間比Minor GC要長掏膏。所以如果執(zhí)行太長時間的Full GC(超過1秒),就會發(fā)生超時錯誤
- 如果你試著減少老年代的大小來降低Full GC的執(zhí)行時間敦锌,可能會引發(fā)OutOfMemoryError或者導致Full GC的頻率升高馒疹。
- 如果是通過增加老年代的大小來降低Full GC的頻率,執(zhí)行時間將會增加乙墙。
影響GC的參數(shù)
JVM調(diào)優(yōu)主要用到參數(shù)羅列在下面的兩張表中颖变。主要分為內(nèi)存參數(shù)和垃圾類型參數(shù)。GC優(yōu)化的過程就是在調(diào)試這些參數(shù)的過程听想。
下表是與JVM內(nèi)存相關的參數(shù):
比較常用的參數(shù)是 -Xms
, -Xmx
和 -XX:NewRatio
下表展示的垃圾收集器類型的可選參數(shù):
不同的垃圾回收器與老年代年輕代的關系如下:
還有一個常用的參數(shù)是-XX:+PrintGCDetails
通過 -XX:+PrintGCDetails
可以查看具體的GC日志腥刹。下面的兩張圖分別介紹Full GC與Minor GC日志里的各個字段。
監(jiān)控命令
設置好上面將的參數(shù)后汉买,可以通過監(jiān)控查看我們優(yōu)化的衔峰。監(jiān)控可以分為命令監(jiān)控和圖形化監(jiān)控。關于圖形化監(jiān)控可用工具比較常見的有JConsole和VisualVM,可以參考這篇文章垫卤。這里不做過多介紹邻吞。這節(jié)主要介紹個常用的監(jiān)控命令,在下面的案例中也是有用到的葫男。
jps 命令格式:jps [option] [hostid]
jps命令用于查詢正在運行的JVM進程,常用的參數(shù)為:
-q:只輸出LVMID崔列,省略主類的名稱
-m:輸出虛擬機進程啟動時傳給主類main()函數(shù)的參數(shù)
-l:輸出主類的全類名梢褐,如果進程執(zhí)行的是Jar包,輸出Jar路徑
-v:輸出虛擬機進程啟動時JVM參數(shù)
例子:
jstack 命令格式:jstack [option] vmid
用于生成當前JVM的所有線程快照赵讯,線程快照是虛擬機每一條線程正在執(zhí)行的方法,目的是定位線程出現(xiàn)長時間停頓的原因盈咳。
-F:當正常輸出的請求不被響應時,強制輸出線程堆棧
-l:除堆棧外边翼,顯示關于鎖的附加信息
-m:如果調(diào)用到本地方法的話鱼响,可以顯示C/C++的堆棧
例子
jmap 命令格式:jmap [option] vmid
用于顯示當前Java堆和永久代的詳細信息(如當前使用的收集器,當前的空間使用率等)
-dump:生成java堆轉(zhuǎn)儲快照
-heap:顯示java堆詳細信息(只在Linux/Solaris下有效)
-F:當虛擬機進程對-dump選項沒有響應時组底,可使用這個選項強制生成dump快照(只在Linux/Solaris下有效)
-histo:顯示堆中對象統(tǒng)計信息
例子
jstat命令格式:jstat [option vmid [interval[s|ms] [count]]]
jstat可以實時顯示本地或遠程JVM進程中類裝載丈积、內(nèi)存、垃圾收集债鸡、JIT編譯等數(shù)據(jù)
-class:監(jiān)視類裝載江滨、卸載數(shù)量、總空間及類裝載所耗費的時間
-gc:監(jiān)聽Java堆狀況厌均,包括Eden區(qū)唬滑、兩個Survivor區(qū)、老年代棺弊、永久代等的容量晶密,以用空間、GC時間合計等信息
-gccapacity:監(jiān)視內(nèi)容與-gc基本相同模她,但輸出主要關注java堆各個區(qū)域使用到的最大和最小空間
-gcutil:監(jiān)視內(nèi)容與-gc基本相同稻艰,但輸出主要關注已使用空間占總空間的百分比
-gccause:與-gcutil功能一樣,但是會額外輸出導致上一次GC產(chǎn)生的原因
-gcnew:監(jiān)視新生代GC狀況
-gcnewcapacity:監(jiān)視內(nèi)同與-gcnew基本相同侈净,輸出主要關注使用到的最大和最小空間
-gcold:監(jiān)視老年代GC情況
-gcoldcapacity:監(jiān)視內(nèi)同與-gcold基本相同连锯,輸出主要關注使用到的最大和最小空間
-gcpermcapacity:輸出永久代使用到最大和最小空間
-compiler:輸出JIT編譯器編譯過的方法、耗時等信息
例子
命令jstat -gc 309 1000 5代表著:搜集vid為309的java進程的整體gc狀態(tài)用狱, 每1000ms收集一次运怖,共收集5次
案例
下面兩個案例是網(wǎng)友的JVM調(diào)優(yōu)過程,作者過程思路清晰夏伊,步步分析到位摇展,在這里分享給大家:
1、CMS調(diào)優(yōu)
2溺忧、OOM問題調(diào)優(yōu)
總結(jié)
JVM調(diào)優(yōu)在實際工作中用到的比較少咏连,但是這也是作為java程序員必須掌握的基本技能盯孙。真正熟練的使用GC調(diào)優(yōu),是建立在多次進行GC監(jiān)控和調(diào)優(yōu)的實戰(zhàn)經(jīng)驗上的祟滴。
下面羅列了幾個數(shù)據(jù)作為參考振惰,如果GC執(zhí)行時間滿足下列所有條件,就沒有必要進行GC優(yōu)化了:
Minor GC執(zhí)行非常迅速(50ms以內(nèi))
Minor GC沒有頻繁執(zhí)行(大約10s執(zhí)行一次)
Full GC執(zhí)行非常迅速(1s以內(nèi))
Full GC沒有頻繁執(zhí)行(大約10min執(zhí)行一次)
PS:如果你要應付面試上的JVM題目垄懂。這可以參考這篇文章jvm知識點總覽