被虐后,分享JVM調(diào)優(yōu)原理相關(guān)的知識和經(jīng)驗
本文只介紹一些原理和思路的內(nèi)容,希望對你與所幫助!
先了解原理,然后在進行調(diào)優(yōu)。
一定要記住的是任何的調(diào)優(yōu)都不是一蹴而就,不要指望改動一個參數(shù)就達到調(diào)優(yōu)的目的,也不要僅僅改動了一個參數(shù),就認為是做了調(diào)優(yōu)。調(diào)優(yōu)是在已有的資源和要達到的目標的前提上,進行權(quán)衡。
從類加載到整個JVM的運行周期內(nèi)大致流程和結(jié)構(gòu)如下:
從上圖可以看到,JVM 可以劃分為這些部分:
執(zhí)行引擎,包括:GC、JIT 編譯器
類加載子系統(tǒng),這部分的問題,一般在開發(fā)過程中出現(xiàn)
JNI 部分,這部分問題一般在 JVM 之外
運行時數(shù)據(jù)區(qū);Java 將內(nèi)存分為 2 大塊:堆內(nèi)存和棧內(nèi)存
首先我們要對上述的內(nèi)容有一定的了解,從全局出發(fā)??戳松蠄D,在調(diào)優(yōu)中我們能做的也就是對運行時數(shù)據(jù)區(qū)進行一些操作,然后選擇執(zhí)行引擎用何種垃圾收集器對垃圾進行回收。
1、本文調(diào)優(yōu)思路只針對JVM1.8,先看下JVM1.8內(nèi)存模型
注意:JVM 線程占用的是系統(tǒng)空間,所以當JVM的堆內(nèi)存越大,系統(tǒng)本身的內(nèi)存就越少,自然可生成的線程數(shù)量就越少。
2、JVM調(diào)優(yōu),主要從兩個方面考慮:堆內(nèi)存大小配置和垃圾回收算法選擇
- # 設(shè)置堆內(nèi)存
 - -Xmx4g -Xms4g
 - # 指定 GC 算法
 - -XX:+UseG1GC -XX:MaxGCPauseMillis=50
 - # 指定 GC 并行線程數(shù)
 - -XX:ParallelGCThreads=4
 - # 打印 GC 日志
 - -XX:+PrintGCDetails -XX:+PrintGCDateStamps
 - # 指定 GC 日志文件
 - -Xloggc:gc.log
 - # 指定 Meta 區(qū)的最大值
 - -XX:MaxMetaspaceSize=2g
 - # 設(shè)置單個線程棧的大小
 - -Xss1m
 - # 指定堆內(nèi)存溢出時自動進行 Dump
 - -XX:+HeapDumpOnOutOfMemoryError
 - -XX:HeapDumpPath=/usr/local
 
3、JVM - GC類型組合以及適用場合
垃圾回收,分為Young區(qū)垃圾回收器,以及Old區(qū)垃圾回收器兩部分,兩部分需要組合使用:
新生代收集器:Serial、ParNew、Parallel Scavenge;
老年代收集器:Serial Old、Parallel Old、CMS;
serial:新生代收集器,是最早出現(xiàn)最成熟的收集器,單線程,獨占式,GC時會stop the world 應(yīng)用暫停。
ParNew :新生代收集器,是Serial 收集器的多線程版本,獨占式,同樣地GC時會stop the world 應(yīng)用暫停。
Parallel Scavenge:新生代收集器, 獨占式, 與ParNew相似,特點:吞吐量優(yōu)先
CMS:(concurrent-mark-sweep),老年代收集器,非獨占式,多個線程,標記并清除算法,響應(yīng)時間優(yōu)先,缺點是GC后不進行內(nèi)存整理,會有內(nèi)存碎片。
Serial Old:是Serial 的老年代版本,同樣是單線程收集器, 獨占式
Parallel Old 是Parallel Scavenge 收集器的老年代版本,多線程收集, 獨占式
G1:備受期待的新一代垃圾收集器,可預(yù)測的停頓:這又是G1相對于CMS的一大優(yōu)勢,結(jié)合了Parallel Scavenge以及CMS兩種收集器的優(yōu)點,又摒棄了其缺陷, 非獨占式
收集器的好壞,主要有兩個指標:停頓時間和吞吐量
GC 選擇的經(jīng)驗總結(jié)
綜合來看,G1 是 JDK11 之前 HotSpot JVM 中最先進的準產(chǎn)品級(production-ready) 垃圾收集器。重要的是,HotSpot 工程師的主要精力都放在不斷改進 G1 上面。在更新的 JDK 版本中,將會帶來更多強大的功能和優(yōu)化。
可以看到,G1 作為 CMS 的代替者出現(xiàn),解決了 CMS 中的各種疑難問題,包括暫停時間的可預(yù)測性,并終結(jié)了堆內(nèi)存的碎片化。對單業(yè)務(wù)延遲非常敏感的系統(tǒng)來說,如果 CPU 資源不受限制,那么 G1 可以說是 HotSpot 中最好的選擇,特別是在最新版本的 JVM 中。當然這種降低延遲的優(yōu)化也不是沒有代價的:由于額外的寫屏障和守護線程,G1 的開銷會更大。如果系統(tǒng)屬于吞吐量優(yōu)先型的,又或者 CPU 持續(xù)占用 100%,而又不在乎單次 GC 的暫停時間,那么 CMS 是更好的選擇。
總之,G1 適合大內(nèi)存,需要較低延遲的場景。
選擇正確的 GC 算法,唯一可行的方式就是去嘗試,并找出不合理的地方,一般性的指導(dǎo)原則:
- 如果系統(tǒng)考慮吞吐優(yōu)先,CPU 資源都用來最大程度處理業(yè)務(wù),用 Parallel GC;
 - 如果系統(tǒng)考慮低延遲有限,每次 GC 時間盡量短,用 CMS GC;
 - 如果系統(tǒng)內(nèi)存堆較大,同時希望整體來看平均 GC 時間可控,使用 G1 GC。
 
對于內(nèi)存大小的考量:
- 一般 4G 以上,算是比較大,用 G1 的性價比較高。
 - 一般超過 8G,比如 16G-64G 內(nèi)存,非常推薦使用 G1 GC。
 
最后討論一個很多開發(fā)者經(jīng)常忽視的問題,也是面試大廠常問的問題:
JDK 8 的默認 GC 是什么?
很多人或覺得是 CMS,甚至 G1,其實都不是。
答案是:JDK 8并行 GC 是 JDK8 里的默認 GC 策略。默認使用的是 Parallel Scavenge (新生代) 和 Parallel Old (老年代),基于我的Jdk 1.8.0_181-b13版本
注意,G1 成為 JDK9 以后版本的默認 GC 策略,同時,ParNew + SerialOld 這種組合不被支持。
最后在分享一下,調(diào)優(yōu)后如果還遇到JVM相關(guān)問題,請記住十六字箴言:做好監(jiān)控,定位問題,驗證結(jié)果,總結(jié)歸納。






















 
 
 



 
 
 
 