jvm GC參數(shù)調(diào)優(yōu)

(本篇主要以CMS-GC為主,如果對(duì)G1感興趣的我后面也可以補(bǔ)充)

一、GC參數(shù)說明與介紹

1.1 jvm啟動(dòng)參數(shù)分為三類:

類型 描述
第一類是標(biāo)準(zhǔn)參數(shù)(-) 所有的JVM實(shí)現(xiàn)都必須實(shí)現(xiàn)這些參數(shù)的功能,而且向后兼容
第二類是非標(biāo)準(zhǔn)參數(shù)(-X) 默認(rèn)jvm實(shí)現(xiàn)這些參數(shù)的功能,但是并不保證所有jvm實(shí)現(xiàn)都滿足,且不保證向后兼容
第三類是非Stable參數(shù)(-XX) 此類參數(shù)各個(gè)jvm實(shí)現(xiàn)會(huì)有所不同,將來可能會(huì)隨時(shí)取消,需要慎重使用;此類參數(shù)各個(gè)jvm實(shí)現(xiàn)會(huì)有所不同,將來可能會(huì)隨時(shí)取消,需要慎重使用

1.2 jvm內(nèi)存相關(guān)的配置


1.3 GC收集器相關(guān)的配置


1.4 通用參數(shù)相關(guān)的配置


1.5 CMS重要配置參數(shù)


1.6 日志輸出相關(guān)配置參數(shù)


二、STW指標(biāo)(重點(diǎn))

眾所周知CMS-GC是采用標(biāo)記清除算法, CMS-GC主要分成7個(gè)步驟:

  • 初始標(biāo)記(Initial Mark):為了收集應(yīng)用程序的對(duì)象引用需要暫停應(yīng)用程序線程,該階段完成后,應(yīng)用程序線程再次啟動(dòng)。

  • 并發(fā)標(biāo)記(Concurrent Mark):從第一階段收集到的對(duì)象引用開始,遍歷所有其他的對(duì)象引用。

  • 并發(fā)預(yù)清理(Concurrent Preclean):改變當(dāng)運(yùn)行第二階段時(shí),由應(yīng)用程序線程產(chǎn)生的對(duì)象引用,以更新第二階段的結(jié)果。

  • 可終止的并發(fā)預(yù)清理(Concurrent Abortable Preclean):這個(gè)階段嘗試著去承擔(dān)STW的Final Remark階段足夠多的工作。這個(gè)階段持續(xù)的時(shí)間依賴好多的因素,由于這個(gè)階段是重復(fù)的做相同的事情直到發(fā)生aboart的條件(比如:重復(fù)的次數(shù)、多少量的工作、持續(xù)的時(shí)間等等)之一才會(huì)停止

  • 重新標(biāo)記(Final Remark):最后一個(gè)STW的階段, 在這里所有不再被應(yīng)用的對(duì)象將從堆里被清除掉。這個(gè)階段會(huì)標(biāo)記老年代全部的存活對(duì)象,包括那些在并發(fā)標(biāo)記階段更改的或者新創(chuàng)建的引用對(duì)象. 盡管先前的pre clean階段盡量應(yīng)對(duì)處理了并發(fā)運(yùn)行時(shí)用戶線程改變的對(duì)象應(yīng)用的標(biāo)記,但是不可能跟上對(duì)象改變的速度,只是為final remark階段盡量減少了負(fù)擔(dān)

  • 并發(fā)清理(Concurrent Sweep):這個(gè)階段主要是清除那些沒有標(biāo)記的對(duì)象并且回收空間

  • 并發(fā)重置(Concurrent Reset):收集器做一些收尾的工作,以便下一次GC周期能有一個(gè)干凈的狀態(tài)。

其中5個(gè)階段(名字以Concurrent開始的)與實(shí)際的應(yīng)用程序是并發(fā)執(zhí)行的,而其他2個(gè)階段需要暫停應(yīng)用程序線程(STW), 而且STW時(shí)間主要耗時(shí)在remark階段(大約占了80%)

下面是我截的一臺(tái)應(yīng)用服務(wù)器當(dāng)時(shí)的GC日志圖, 我們的告警里面的STW時(shí)間實(shí)際是統(tǒng)計(jì)只有remark階段的耗時(shí),其實(shí)并沒有統(tǒng)計(jì)初始標(biāo)記階段的耗時(shí),嚴(yán)格意義上來說是不準(zhǔn)確的,換句話來說實(shí)際我們的STW時(shí)間更長(zhǎng)遠(yuǎn)超了810毫秒.



其實(shí)從耗時(shí)上我們可以看出, 主要耗時(shí)在處理引用上面,看JVM源碼,weak refs processing主要包括SoftReference、WeakReference、FinalReference、PhantomReference以及JNI Weak Reference這五種Reference對(duì)象的處理,處理的主要內(nèi)容是對(duì)之前標(biāo)記的Reference對(duì)象重新處理,重新判斷是否需要標(biāo)記(不標(biāo)記就是要回收的),如果不標(biāo)記就需要放到refqueue里,等待java ReferenceHandler線程處理。
所以從工作的主要內(nèi)容看各種Reference的處理時(shí)間跟reference的個(gè)數(shù)成正比。那么接下來我們需要做的是確定是哪種Reference比較耗時(shí),然后進(jìn)行針對(duì)性優(yōu)化。所以加了個(gè)參數(shù)-XX:+PrintReferenceGC,來具體顯示各種Reference的個(gè)數(shù)和處理時(shí)間。

三、重要參數(shù)的調(diào)整:

  • 3.1 -XX:+CMSScavengeBeforeRemark
    在CMS GC前啟動(dòng)一次young Gc,目的在于減少old Gen對(duì)young Gc Gen的引用, 降低remark階段的開銷, 但是也存在一些情況不生效:
    下面是笨神的回復(fù):




  • 3.2 -XX:+ParallelRefProcEnabled
    并行處理Reference,加快處理速度,縮短耗時(shí)

  • 3.3 我看到也有同事 配置了-XX:CMSInitiatingOccupancyFraction=55 , 調(diào)小了參數(shù)想提前CMS啟動(dòng)垃圾回收, 這個(gè)視情況而定, 有可能會(huì)增加GC的頻次.關(guān)于CMSInitiatingOccupancyFraction這個(gè)參數(shù)(標(biāo)志來命令JVM不基于運(yùn)行時(shí)收集的數(shù)據(jù)來啟動(dòng)CMS垃圾收集周期),如果你搭配了UseCMSInitiatingOccupancyOnly一起使用, 它會(huì)每次都按照這個(gè)域值去觸發(fā), 不然垃圾回收器會(huì)根據(jù)自己的決策去浮動(dòng)(只有第一次按照這個(gè)值去觸發(fā)). 所以這里想說明的一點(diǎn)是, 你的參數(shù)設(shè)置如果沒有基于大量的測(cè)試, 還不如交由CMS自己去做決策.

四、什么是FullGC

  • 觸發(fā)STW的才是真正的FullGC, 這個(gè)容易和Major Gc混淆, 因?yàn)镸ajor Gc的作用域僅僅只是老年代空間

五、 FullGC發(fā)生的條件

  • 調(diào)用System.gc
  • 老年代空間不足
  • 永久代空間不足
  • gc 擔(dān)保失敗
  • Cocurrent mode failure

六、可以發(fā)布其中一臺(tái)服務(wù)后做橫向?qū)Ρ?觀察一段時(shí)間

七、選型

如果是對(duì)于大內(nèi)存的服務(wù), 比如你的服務(wù)器是16G、32G其實(shí)完全可以用G1, 目前G1也是java9默認(rèn)的垃圾回收器,G1在內(nèi)存不吃緊的情況下會(huì)可以做到靈活回收和大大降低大堆停頓.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容