GC原理及調(diào)優(yōu)

本文介紹 GC 基礎(chǔ)原理和理論吧兔,GC 調(diào)優(yōu)方法思路和方法鳖链,基于 Hotspot jdk1.8葛超,學(xué)習(xí)之后你將了解如何對(duì)生產(chǎn)系統(tǒng)出現(xiàn)的 GC 問題進(jìn)行排查解決祝蝠。

老大難的GC原理及調(diào)優(yōu)音诈,這全說清楚了

圖片來自 Pexels

內(nèi)容主要如下:

  • GC 基礎(chǔ)原理,涉及調(diào)優(yōu)目標(biāo)绎狭,GC 事件分類细溅、JVM 內(nèi)存分配策略、GC 日志分析等儡嘶。
  • CMS 原理及調(diào)優(yōu)喇聊。
  • G1 原理及調(diào)優(yōu)。
  • GC 問題排查和解決思路蹦狂。

GC 基礎(chǔ)原理

GC 調(diào)優(yōu)目標(biāo)

大多數(shù)情況下對(duì) Java 程序進(jìn)行 GC 調(diào)優(yōu)誓篱,主要關(guān)注兩個(gè)目標(biāo):

  • 響應(yīng)速度(Responsiveness):響應(yīng)速度指程序或系統(tǒng)對(duì)一個(gè)請(qǐng)求的響應(yīng)有多迅速。

比如凯楔,用戶訂單查詢響應(yīng)時(shí)間窜骄,對(duì)響應(yīng)速度要求很高的系統(tǒng),較大的停頓時(shí)間是不可接受的摆屯。調(diào)優(yōu)的重點(diǎn)是在短的時(shí)間內(nèi)快速響應(yīng)啊研。

  • 吞吐量(Throughput):吞吐量關(guān)注在一個(gè)特定時(shí)間段內(nèi)應(yīng)用系統(tǒng)的最大工作量。

例如每小時(shí)批處理系統(tǒng)能完成的任務(wù)數(shù)量鸥拧,在吞吐量方面優(yōu)化的系統(tǒng),較長(zhǎng)的 GC 停頓時(shí)間也是可以接受的削解,因?yàn)楦咄掏铝繎?yīng)用更關(guān)心的是如何盡可能快地完成整個(gè)任務(wù)富弦,不考慮快速響應(yīng)用戶請(qǐng)求。

GC 調(diào)優(yōu)中氛驮,GC 導(dǎo)致的應(yīng)用暫停時(shí)間影響系統(tǒng)響應(yīng)速度腕柜,GC 處理線程的 CPU 使用率影響系統(tǒng)吞吐量。

GC 分代收集算法

現(xiàn)代的垃圾收集器基本都是采用分代收集算法矫废,其主要思想: 將 Java 的堆內(nèi)存邏輯上分成兩塊:新生代盏缤、老年代,針對(duì)不同存活周期蓖扑、不同大小的對(duì)象采取不同的垃圾回收策略唉铜。

老大難的GC原理及調(diào)優(yōu),這全說清楚了

新生代(Young Generation)

新生代又叫年輕代律杠,大多數(shù)對(duì)象在新生代中被創(chuàng)建潭流,很多對(duì)象的生命周期很短竞惋。

每次新生代的垃圾回收(又稱 Young GC、Minor GC灰嫉、YGC)后只有少量對(duì)象存活拆宛,所以使用復(fù)制算法,只需少量的復(fù)制操作成本就可以完成回收讼撒。

新生代內(nèi)又分三個(gè)區(qū):一個(gè) Eden 區(qū)浑厚,兩個(gè) Survivor 區(qū)(S0、S1根盒,又稱From Survivor钳幅、To Survivor),大部分對(duì)象在 Eden 區(qū)中生成郑象。

當(dāng) Eden 區(qū)滿時(shí)贡这,還存活的對(duì)象將被復(fù)制到兩個(gè) Survivor 區(qū)(中的一個(gè));當(dāng)這個(gè) Survivor 區(qū)滿時(shí),此區(qū)的存活且不滿足晉升到老年代條件的對(duì)象將被復(fù)制到另外一個(gè) Survivor 區(qū)厂榛。

對(duì)象每經(jīng)歷一次復(fù)制盖矫,年齡加 1,達(dá)到晉升年齡閾值后击奶,轉(zhuǎn)移到老年代辈双。

老年代(Old Generation)

在新生代中經(jīng)歷了 N 次垃圾回收后仍然存活的對(duì)象,就會(huì)被放到老年代柜砾,該區(qū)域中對(duì)象存活率高湃望。老年代的垃圾回收通常使用“標(biāo)記-整理”算法。

GC 事件分類

根據(jù)垃圾收集回收的區(qū)域不同痰驱,垃圾收集主要分為:

  • Young GC
  • Old GC
  • Full GC
  • Mixed GC

①Young GC

新生代內(nèi)存的垃圾收集事件稱為 Young GC(又稱 Minor GC)证芭,當(dāng) JVM 無法為新對(duì)象分配在新生代內(nèi)存空間時(shí)總會(huì)觸發(fā) Young GC。

比如 Eden 區(qū)占滿時(shí)担映,新對(duì)象分配頻率越高废士,Young GC 的頻率就越高。

Young GC 每次都會(huì)引起全線停頓(Stop-The-World)蝇完,暫停所有的應(yīng)用線程官硝,停頓時(shí)間相對(duì)老年代 GC 造成的停頓,幾乎可以忽略不計(jì)短蜕。

②Old GC 氢架、Full GC、Mixed GC

Old GC:只清理老年代空間的 GC 事件朋魔,只有 CMS 的并發(fā)收集是這個(gè)模式岖研。

Full GC:清理整個(gè)堆的 GC 事件,包括新生代警检、老年代缎玫、元空間等 硬纤。

Mixed GC:清理整個(gè)新生代以及部分老年代的 GC,只有 G1 有這個(gè)模式赃磨。

GC 日志分析

GC 日志是一個(gè)很重要的工具筝家,它準(zhǔn)確記錄了每一次的 GC 的執(zhí)行時(shí)間和執(zhí)行結(jié)果,通過分析 GC 日志可以調(diào)優(yōu)堆設(shè)置和 GC 設(shè)置邻辉,或者改進(jìn)應(yīng)用程序的對(duì)象分配模式溪王。

開啟的 JVM 啟動(dòng)參數(shù)如下:

-verbose:gc 
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-XX:+PrintGCTimeStamps 

常見的 Young GC、Full GC 日志含義如下:

老大難的GC原理及調(diào)優(yōu)值骇,這全說清楚了
老大難的GC原理及調(diào)優(yōu)莹菱,這全說清楚了

免費(fèi)的 GC 日志圖形分析工具推薦下面 2 個(gè):

  • GCViewer,下載 jar 包直接運(yùn)行 吱瘩。
  • gceasy道伟,Web 工具,上傳 GC 日志在線使用使碾。

內(nèi)存分配策略

Java 提供的自動(dòng)內(nèi)存管理蜜徽,可以歸結(jié)為解決了對(duì)象的內(nèi)存分配和回收的問題。

前面已經(jīng)介紹了內(nèi)存回收票摇,下面介紹幾條最普遍的內(nèi)存分配策略:

①對(duì)象優(yōu)先在 Eden 區(qū)分配:大多數(shù)情況下拘鞋,對(duì)象在先新生代 Eden 區(qū)中分配。當(dāng) Eden 區(qū)沒有足夠空間進(jìn)行分配時(shí)矢门,虛擬機(jī)將發(fā)起一次 Young GC盆色。

②大對(duì)象之間進(jìn)入老年代:JVM 提供了一個(gè)對(duì)象大小閾值參數(shù)(
-XX:PretenureSizeThreshold,默認(rèn)值為 0祟剔,代表不管多大都是先在 Eden 中分配內(nèi)存)隔躲。

大于參數(shù)設(shè)置的閾值值的對(duì)象直接在老年代分配,這樣可以避免對(duì)象在 Eden 及兩個(gè) Survivor 直接發(fā)生大內(nèi)存復(fù)制物延。

③長(zhǎng)期存活的對(duì)象將進(jìn)入老年代:對(duì)象每經(jīng)歷一次垃圾回收蹭越,且沒被回收掉,它的年齡就增加 1教届,大于年齡閾值參數(shù)(-XX:MaxTenuringThreshold,默認(rèn) 15)的對(duì)象驾霜,將晉升到老年代中案训。

④空間分配擔(dān)保:當(dāng)進(jìn)行 Young GC 之前,JVM 需要預(yù)估:老年代是否能夠容納 Young GC 后新生代晉升到老年代的存活對(duì)象粪糙,以確定是否需要提前觸發(fā) GC 回收老年代空間强霎,基于空間分配擔(dān)保策略來計(jì)算。

continueSize蓉冈,老年代最大可用連續(xù)空間:

老大難的GC原理及調(diào)優(yōu)城舞,這全說清楚了

Young GC 之后如果成功(Young GC 后晉升對(duì)象能放入老年代)轩触,則代表擔(dān)保成功,不用再進(jìn)行 Full GC家夺,提高性能脱柱。

如果失敗,則會(huì)出現(xiàn)“promotion failed”錯(cuò)誤拉馋,代表擔(dān)保失敗榨为,需要進(jìn)行 Full GC。

⑤動(dòng)態(tài)年齡判定:新生代對(duì)象的年齡可能沒達(dá)到閾值(MaxTenuringThreshold 參數(shù)指定)就晉升老年代煌茴。

如果 Young GC 之后随闺,新生代存活對(duì)象達(dá)到相同年齡所有對(duì)象大小的總和大于任意 Survivor 空間(S0+S1空間)的一半,此時(shí) S0 或者 S1 區(qū)即將容納不了存活的新生代對(duì)象蔓腐。

年齡大于或等于該年齡的對(duì)象就可以直接進(jìn)入老年代矩乐,無須等到 MaxTenuringThreshold 中要求的年齡。

另外回论,如果 Young GC 后 S0 或 S1 區(qū)不足以容納:未達(dá)到晉升老年代條件的新生代存活對(duì)象散罕,會(huì)導(dǎo)致這些存活對(duì)象直接進(jìn)入老年代,需要盡量避免透葛。

CMS 原理及調(diào)優(yōu)

名詞解釋

可達(dá)性分析算法:用于判斷對(duì)象是否存活笨使,基本思想是通過一系列稱為“GC Root”的對(duì)象作為起點(diǎn)(常見的 GC Root 有系統(tǒng)類加載器、棧中的對(duì)象僚害、處于激活狀態(tài)的線程等)硫椰,基于對(duì)象引用關(guān)系,從 GC Roots 開始向下搜索萨蚕,所走過的路徑稱為引用鏈靶草,當(dāng)一個(gè)對(duì)象到 GC Root 沒有任何引用鏈相連,證明對(duì)象不再存活岳遥。

Stop The World:GC 過程中分析對(duì)象引用關(guān)系奕翔,為了保證分析結(jié)果的準(zhǔn)確性,需要通過停頓所有 Java 執(zhí)行線程浩蓉,保證引用關(guān)系不再動(dòng)態(tài)變化派继,該停頓事件稱為 Stop The World(STW)。

Safepoint:代碼執(zhí)行過程中的一些特殊位置捻艳,當(dāng)線程執(zhí)行到這些位置的時(shí)候驾窟,說明虛擬機(jī)當(dāng)前的狀態(tài)是安全的,如果有需要 GC认轨,線程可以在這個(gè)位置暫停绅络。

HotSpot 采用主動(dòng)中斷的方式,讓執(zhí)行線程在運(yùn)行期輪詢是否需要暫停的標(biāo)志,若需要?jiǎng)t中斷掛起恩急。

CMS 簡(jiǎn)介

CMS(Concurrent Mark and Sweep 并發(fā)-標(biāo)記-清除)杉畜,是一款基于并發(fā)、使用標(biāo)記清除算法的垃圾回收算法衷恭,只針對(duì)老年代進(jìn)行垃圾回收此叠。

CMS 收集器工作時(shí),盡可能讓 GC 線程和用戶線程并發(fā)執(zhí)行匾荆,以達(dá)到降低 STW 時(shí)間的目的拌蜘。

通過以下命令行參數(shù),啟用 CMS 垃圾收集器:

-XX:+UseConcMarkSweepGC 

值得補(bǔ)充的是牙丽,下面介紹到的 CMS GC 是指老年代的 GC简卧,而 Full GC 指的是整個(gè)堆的 GC 事件,包括新生代烤芦、老年代举娩、元空間等,兩者有所區(qū)分构罗。

新生代垃圾回收

能與 CMS 搭配使用的新生代垃圾收集器有 Serial 收集器和 ParNew 收集器铜涉。

這 2 個(gè)收集器都采用標(biāo)記復(fù)制算法,都會(huì)觸發(fā) STW 事件遂唧,停止所有的應(yīng)用線程芙代。不同之處在于,Serial 是單線程執(zhí)行盖彭,ParNew 是多線程執(zhí)行纹烹。

老大難的GC原理及調(diào)優(yōu),這全說清楚了

老年代垃圾回收

老大難的GC原理及調(diào)優(yōu)召边,這全說清楚了

CMS GC 以獲取最小停頓時(shí)間為目的铺呵,盡可能減少 STW 時(shí)間,可以分為 7 個(gè)階段:

階段 1:初始標(biāo)記(Initial Mark)

老大難的GC原理及調(diào)優(yōu)隧熙,這全說清楚了

此階段的目標(biāo)是標(biāo)記老年代中所有存活的對(duì)象, 包括 GC Root 的直接引用, 以及由新生代中存活對(duì)象所引用的對(duì)象片挂,觸發(fā)第一次 STW 事件。

這個(gè)過程是支持多線程的(JDK7 之前單線程贞盯,JDK8 之后并行音念,可通過參數(shù)
CMSParallelInitialMarkEnabled 調(diào)整)。

階段 2:并發(fā)標(biāo)記(Concurrent Mark)

老大難的GC原理及調(diào)優(yōu)躏敢,這全說清楚了

此階段 GC 線程和應(yīng)用線程并發(fā)執(zhí)行闷愤,遍歷階段 1 初始標(biāo)記出來的存活對(duì)象,然后繼續(xù)遞歸標(biāo)記這些對(duì)象可達(dá)的對(duì)象父丰。

階段 3:并發(fā)預(yù)清理(Concurrent Preclean)

老大難的GC原理及調(diào)優(yōu),這全說清楚了

此階段 GC 線程和應(yīng)用線程也是并發(fā)執(zhí)行,因?yàn)殡A段 2 是與應(yīng)用線程并發(fā)執(zhí)行蛾扇,可能有些引用關(guān)系已經(jīng)發(fā)生改變攘烛。

通過卡片標(biāo)記(Card Marking),提前把老年代空間邏輯劃分為相等大小的區(qū)域(Card)镀首。

如果引用關(guān)系發(fā)生改變坟漱,JVM 會(huì)將發(fā)生改變的區(qū)域標(biāo)記為“臟區(qū)”(Dirty Card),然后在本階段更哄,這些臟區(qū)會(huì)被找出來芋齿,刷新引用關(guān)系,清除“臟區(qū)”標(biāo)記成翩。

階段 4:并發(fā)可取消的預(yù)清理(Concurrent Abortable Preclean)

此階段也不停止應(yīng)用線程觅捆。本階段嘗試在 STW 的最終標(biāo)記階段(Final Remark)之前盡可能地多做一些工作,以減少應(yīng)用暫停時(shí)間麻敌。

在該階段不斷循環(huán)處理:標(biāo)記老年代的可達(dá)對(duì)象栅炒、掃描處理 Dirty Card 區(qū)域中的對(duì)象,循環(huán)的終止條件有:

  • 達(dá)到循環(huán)次數(shù)
  • 達(dá)到循環(huán)執(zhí)行時(shí)間閾值
  • 新生代內(nèi)存使用率達(dá)到閾值

階段 5:最終標(biāo)記(Final Remark)

這是 GC 事件中第二次(也是最后一次)STW 階段术羔,目標(biāo)是完成老年代中所有存活對(duì)象的標(biāo)記赢赊。

在此階段執(zhí)行:

  • 遍歷新生代對(duì)象,重新標(biāo)記
  • 根據(jù) GC Roots级历,重新標(biāo)記
  • 遍歷老年代的 Dirty Card释移,重新標(biāo)記

階段 6:并發(fā)清除(Concurrent Sweep)

老大難的GC原理及調(diào)優(yōu),這全說清楚了

此階段與應(yīng)用程序并發(fā)執(zhí)行寥殖,不需要 STW 停頓玩讳,根據(jù)標(biāo)記結(jié)果清除垃圾對(duì)象。

階段 7:并發(fā)重置(Concurrent Reset)

此階段與應(yīng)用程序并發(fā)執(zhí)行扛禽,重置 CMS 算法相關(guān)的內(nèi)部數(shù)據(jù), 為下一次 GC 循環(huán)做準(zhǔn)備锋边。

CMS 常見問題

①最終標(biāo)記階段停頓時(shí)間過長(zhǎng)問題

CMS 的 GC 停頓時(shí)間約 80% 都在最終標(biāo)記階段(Final Remark),若該階段停頓時(shí)間過長(zhǎng)编曼,常見原因是新生代對(duì)老年代的無效引用豆巨,在上一階段的并發(fā)可取消預(yù)清理階段中,執(zhí)行閾值時(shí)間內(nèi)未完成循環(huán)掐场,來不及觸發(fā) Young GC往扔,清理這些無效引用。

通過添加參數(shù):-XX:+CMSScavengeBeforeRemark熊户。

在執(zhí)行最終操作之前先觸發(fā) Young GC萍膛,從而減少新生代對(duì)老年代的無效引用,降低最終標(biāo)記階段的停頓嚷堡。

但如果在上個(gè)階段(并發(fā)可取消的預(yù)清理)已觸發(fā) Young GC蝗罗,也會(huì)重復(fù)觸發(fā) Young GC艇棕。

②并發(fā)模式失敗(concurrent mode failure)&晉升失敗(promotion failed)問題。

老大難的GC原理及調(diào)優(yōu)串塑,這全說清楚了

并發(fā)模式失斦恿稹:當(dāng) CMS 在執(zhí)行回收時(shí),新生代發(fā)生垃圾回收桩匪,同時(shí)老年代又沒有足夠的空間容納晉升的對(duì)象時(shí)打瘪,CMS 垃圾回收就會(huì)退化成單線程的 Full GC。所有的應(yīng)用線程都會(huì)被暫停傻昙,老年代中所有的無效對(duì)象都被回收闺骚。

老大難的GC原理及調(diào)優(yōu),這全說清楚了

晉升失斪钡怠:當(dāng)新生代發(fā)生垃圾回收僻爽,老年代有足夠的空間可以容納晉升的對(duì)象,但是由于空閑空間的碎片化过吻,導(dǎo)致晉升失敗进泼,此時(shí)會(huì)觸發(fā)單線程且?guī)嚎s動(dòng)作的 Full GC。

并發(fā)模式失敗和晉升失敗都會(huì)導(dǎo)致長(zhǎng)時(shí)間的停頓纤虽,常見解決思路如下:

  • 降低觸發(fā) CMS GC 的閾值乳绕。
  • 即參數(shù) -XX:CMSInitiatingOccupancyFraction 的值,讓 CMS GC 盡早執(zhí)行逼纸,以保證有足夠的空間洋措。
  • 增加 CMS 線程數(shù),即參數(shù) -XX:ConcGCThreads杰刽。
  • 增大老年代空間菠发。
  • 讓對(duì)象盡量在新生代回收,避免進(jìn)入老年代贺嫂。

③內(nèi)存碎片問題

通常 CMS 的 GC 過程基于標(biāo)記清除算法滓鸠,不帶壓縮動(dòng)作,導(dǎo)致越來越多的內(nèi)存碎片需要壓縮第喳。

常見以下場(chǎng)景會(huì)觸發(fā)內(nèi)存碎片壓縮:

  • 新生代 Young GC 出現(xiàn)新生代晉升擔(dān)保失敗(promotion failed))
  • 程序主動(dòng)執(zhí)行System.gc()

可通過參數(shù)
CMSFullGCsBeforeCompaction 的值糜俗,設(shè)置多少次 Full GC 觸發(fā)一次壓縮。

默認(rèn)值為 0曲饱,代表每次進(jìn)入 Full GC 都會(huì)觸發(fā)壓縮悠抹,帶壓縮動(dòng)作的算法為上面提到的單線程 Serial Old 算法驳庭,暫停時(shí)間(STW)時(shí)間非常長(zhǎng)吃环,需要盡可能減少壓縮時(shí)間。

G1 原理及調(diào)優(yōu)

G1 簡(jiǎn)介

G1(Garbage-First)是一款面向服務(wù)器的垃圾收集器饲嗽,支持新生代和老年代空間的垃圾收集驻谆,主要針對(duì)配備多核處理器及大容量?jī)?nèi)存的機(jī)器卵凑。

G1 最主要的設(shè)計(jì)目標(biāo)是:實(shí)現(xiàn)可預(yù)期及可配置的 STW 停頓時(shí)間庆聘。

G1 堆空間劃分

老大難的GC原理及調(diào)優(yōu),這全說清楚了

①Region

為實(shí)現(xiàn)大內(nèi)存空間的低停頓時(shí)間的回收勺卢,將劃分為多個(gè)大小相等的 Region掏觉。每個(gè)小堆區(qū)都可能是 Eden 區(qū),Survivor 區(qū)或者 Old 區(qū)值漫,但是在同一時(shí)刻只能屬于某個(gè)代。

在邏輯上, 所有的 Eden 區(qū)和 Survivor 區(qū)合起來就是新生代织盼,所有的 Old 區(qū)合起來就是老年代杨何,且新生代和老年代各自的內(nèi)存 Region 區(qū)域由 G1 自動(dòng)控制,不斷變動(dòng)沥邻。

②巨型對(duì)象

當(dāng)對(duì)象大小超過 Region 的一半危虱,則認(rèn)為是巨型對(duì)象(Humongous Object),直接被分配到老年代的巨型對(duì)象區(qū)(Humongous Regions)唐全。

這些巨型區(qū)域是一個(gè)連續(xù)的區(qū)域集埃跷,每一個(gè) Region 中最多有一個(gè)巨型對(duì)象,巨型對(duì)象可以占多個(gè) Region邮利。

G1 把堆內(nèi)存劃分成一個(gè)個(gè) Region 的意義在于:

  • 每次 GC 不必都去處理整個(gè)堆空間弥雹,而是每次只處理一部分 Region,實(shí)現(xiàn)大容量?jī)?nèi)存的 GC延届。
  • 通過計(jì)算每個(gè) Region 的回收價(jià)值剪勿,包括回收所需時(shí)間、可回收空間方庭,在有限時(shí)間內(nèi)盡可能回收更多的垃圾對(duì)象厕吉,把垃圾回收造成的停頓時(shí)間控制在預(yù)期配置的時(shí)間范圍內(nèi),這也是 G1 名稱的由來:Garbage-First械念。

G1工作模式

針對(duì)新生代和老年代头朱,G1 提供 2 種 GC 模式,Young GC 和 Mixed GC龄减,兩種會(huì)導(dǎo)致 Stop The World项钮。

Young GC:當(dāng)新生代的空間不足時(shí),G1 觸發(fā) Young GC 回收新生代空間欺殿。

Young GC 主要是對(duì) Eden 區(qū)進(jìn)行 GC寄纵,它在 Eden 空間耗盡時(shí)觸發(fā),基于分代回收思想和復(fù)制算法脖苏,每次 Young GC 都會(huì)選定所有新生代的 Region程拭。

同時(shí)計(jì)算下次 Young GC 所需的 Eden 區(qū)和 Survivor 區(qū)的空間,動(dòng)態(tài)調(diào)整新生代所占 Region 個(gè)數(shù)來控制 Young GC 開銷棍潘。

Mixed GC:當(dāng)老年代空間達(dá)到閾值會(huì)觸發(fā) Mixed GC恃鞋,選定所有新生代里的 Region崖媚,根據(jù)全局并發(fā)標(biāo)記階段(下面介紹到)統(tǒng)計(jì)得出收集收益高的若干老年代 Region。

在用戶指定的開銷目標(biāo)范圍內(nèi)恤浪,盡可能選擇收益高的老年代 Region 進(jìn)行 GC畅哑,通過選擇哪些老年代 Region 和選擇多少 Region 來控制 Mixed GC 開銷。

全局并發(fā)標(biāo)記

老大難的GC原理及調(diào)優(yōu)水由,這全說清楚了

全局并發(fā)標(biāo)記主要是為 Mixed GC 計(jì)算找出回收收益較高的 Region 區(qū)域荠呐,具體分為 5 個(gè)階段:

階段 1:初始標(biāo)記(Initial Mark)

暫停所有應(yīng)用線程(STW),并發(fā)地進(jìn)行標(biāo)記從 GC Root 開始直接可達(dá)的對(duì)象(原生棧對(duì)象砂客、全局對(duì)象泥张、JNI 對(duì)象)。

當(dāng)達(dá)到觸發(fā)條件時(shí)鞠值,G1 并不會(huì)立即發(fā)起并發(fā)標(biāo)記周期媚创,而是等待下一次新生代收集,利用新生代收集的 STW 時(shí)間段彤恶,完成初始標(biāo)記钞钙,這種方式稱為借道(Piggybacking)。

階段 2:根區(qū)域掃描(Root Region Scan)

在初始標(biāo)記暫停結(jié)束后声离,新生代收集也完成的對(duì)象復(fù)制到 Survivor 的工作芒炼,應(yīng)用線程開始活躍起來。

此時(shí)為了保證標(biāo)記算法的正確性术徊,所有新復(fù)制到 Survivor 分區(qū)的對(duì)象焕议,需要找出哪些對(duì)象存在對(duì)老年代對(duì)象的引用,把這些對(duì)象標(biāo)記成根(Root)弧关。

這個(gè)過程稱為根分區(qū)掃描(Root Region Scanning)盅安,同時(shí)掃描的 Suvivor 分區(qū)也被稱為根分區(qū)(Root Region)。

根分區(qū)掃描必須在下一次新生代垃圾收集啟動(dòng)前完成(接下來并發(fā)標(biāo)記的過程中世囊,可能會(huì)被若干次新生代垃圾收集打斷)别瞭,因?yàn)槊看?GC 會(huì)產(chǎn)生新的存活對(duì)象集合。

階段 3:并發(fā)標(biāo)記(Concurrent Marking)

標(biāo)記線程與應(yīng)用程序線程并行執(zhí)行株憾,標(biāo)記各個(gè)堆中 Region 的存活對(duì)象信息蝙寨,這個(gè)步驟可能被新的 Young GC 打斷。

所有的標(biāo)記任務(wù)必須在堆滿前就完成掃描嗤瞎,如果并發(fā)標(biāo)記耗時(shí)很長(zhǎng)墙歪,那么有可能在并發(fā)標(biāo)記過程中,又經(jīng)歷了幾次新生代收集贝奇。

階段 4:再次標(biāo)記(Remark)

和 CMS 類似暫停所有應(yīng)用線程(STW)虹菲,以完成標(biāo)記過程短暫地停止應(yīng)用線程, 標(biāo)記在并發(fā)標(biāo)記階段發(fā)生變化的對(duì)象,和所有未被標(biāo)記的存活對(duì)象掉瞳,同時(shí)完成存活數(shù)據(jù)計(jì)算毕源。

階段 5:清理(Cleanup)

為即將到來的轉(zhuǎn)移階段做準(zhǔn)備, 此階段也為下一次標(biāo)記執(zhí)行所有必需的整理計(jì)算工作:

  • 整理更新每個(gè) Region 各自的 RSet(Remember Set浪漠,HashMap 結(jié)構(gòu),記錄有哪些老年代對(duì)象指向本 Region霎褐,key 為指向本 Region 的對(duì)象的引用址愿,value 為指向本 Region 的具體 Card 區(qū)域,通過 RSet 可以確定 Region 中對(duì)象存活信息冻璃,避免全堆掃描)响谓。
  • 回收不包含存活對(duì)象的 Region。
  • 統(tǒng)計(jì)計(jì)算回收收益高(基于釋放空間和暫停目標(biāo))的老年代分區(qū)集合省艳。

G1調(diào)優(yōu)注意點(diǎn)

①Full GC 問題

G1 的正常處理流程中沒有 Full GC歌粥,只有在垃圾回收處理不過來(或者主動(dòng)觸發(fā))時(shí)才會(huì)出現(xiàn),G1 的 Full GC 就是單線程執(zhí)行的 Serial old gc拍埠,會(huì)導(dǎo)致非常長(zhǎng)的 STW,是調(diào)優(yōu)的重點(diǎn)土居,需要盡量避免 Full GC枣购。

常見原因如下:

  • 程序主動(dòng)執(zhí)行 System.gc()
  • 全局并發(fā)標(biāo)記期間老年代空間被填滿(并發(fā)模式失敗)
  • Mixed GC 期間老年代空間被填滿(晉升失敗)
  • Young GC 時(shí) Survivor 空間和老年代沒有足夠空間容納存活對(duì)象

類似 CMS,常見的解決是:

  • 增大 -XX:ConcGCThreads=n 選項(xiàng)增加并發(fā)標(biāo)記線程的數(shù)量擦耀,或者 STW 期間并行線程的數(shù)量:-XX:ParallelGCThreads=n棉圈。
  • 減小 -XX:InitiatingHeapOccupancyPercent 提前啟動(dòng)標(biāo)記周期。
  • 增大預(yù)留內(nèi)存 -XX:G1ReservePercent=n眷蜓,默認(rèn)值是 10分瘾,代表使用 10% 的堆內(nèi)存為預(yù)留內(nèi)存,當(dāng) Survivor 區(qū)域沒有足夠空間容納新晉升對(duì)象時(shí)會(huì)嘗試使用預(yù)留內(nèi)存吁系。

②巨型對(duì)象分配

巨型對(duì)象區(qū)中的每個(gè) Region 中包含一個(gè)巨型對(duì)象德召,剩余空間不再利用,導(dǎo)致空間碎片化汽纤,當(dāng) G1 沒有合適空間分配巨型對(duì)象時(shí)上岗,G1 會(huì)啟動(dòng)串行 Full GC 來釋放空間。

可以通過增加 -XX:G1HeapRegionSize 來增大 Region 大小蕴坪,這樣一來肴掷,相當(dāng)一部分的巨型對(duì)象就不再是巨型對(duì)象了,而是采用普通的分配方式背传。

③不要設(shè)置 Young 區(qū)的大小

原因是為了盡量滿足目標(biāo)停頓時(shí)間呆瞻,邏輯上的 Young 區(qū)會(huì)進(jìn)行動(dòng)態(tài)調(diào)整。如果設(shè)置了大小径玖,則會(huì)覆蓋掉并且會(huì)禁用掉對(duì)停頓時(shí)間的控制痴脾。

④平均響應(yīng)時(shí)間設(shè)置

使用應(yīng)用的平均響應(yīng)時(shí)間作為參考來設(shè)置 MaxGCPauseMillis,JVM 會(huì)盡量去滿足該條件梳星,可能是 90% 的請(qǐng)求或者更多的響應(yīng)時(shí)間在這之內(nèi)明郭, 但是并不代表是所有的請(qǐng)求都能滿足买窟,平均響應(yīng)時(shí)間設(shè)置過小會(huì)導(dǎo)致頻繁 GC。

調(diào)優(yōu)方法與思路

如何分析系統(tǒng) JVM GC 運(yùn)行狀況及合理優(yōu)化?

GC 優(yōu)化的核心思路在于:盡可能讓對(duì)象在新生代中分配和回收薯定,盡量避免過多對(duì)象進(jìn)入老年代始绍,導(dǎo)致對(duì)老年代頻繁進(jìn)行垃圾回收,同時(shí)給系統(tǒng)足夠的內(nèi)存減少新生代垃圾回收次數(shù)话侄,進(jìn)行系統(tǒng)分析和優(yōu)化也是圍繞著這個(gè)思路展開亏推。

分析系統(tǒng)的運(yùn)行狀況

分析系統(tǒng)的運(yùn)行狀況:

  • 系統(tǒng)每秒請(qǐng)求數(shù)、每個(gè)請(qǐng)求創(chuàng)建多少對(duì)象年堆,占用多少內(nèi)存吞杭。
  • Young GC 觸發(fā)頻率、對(duì)象進(jìn)入老年代的速率变丧。
  • 老年代占用內(nèi)存芽狗、Full GC 觸發(fā)頻率、Full GC 觸發(fā)的原因痒蓬、長(zhǎng)時(shí)間 Full GC 的原因童擎。

常用工具如下:

jstat:JVM 自帶命令行工具,可用于統(tǒng)計(jì)內(nèi)存分配速率攻晒、GC 次數(shù)顾复,GC 耗時(shí)。

常用命令格式:

jstat -gc <pid> <統(tǒng)計(jì)間隔時(shí)間> <統(tǒng)計(jì)次數(shù)> 

輸出返回值代表含義如下:

老大難的GC原理及調(diào)優(yōu)鲁捏,這全說清楚了

例如:jstat -gc 32683 1000 10芯砸,統(tǒng)計(jì) pid=32683 的進(jìn)程,每秒統(tǒng)計(jì) 1 次给梅,統(tǒng)計(jì) 10 次假丧。

jmap:JVM 自帶命令行工具,可用于了解系統(tǒng)運(yùn)行時(shí)的對(duì)象分布动羽。

常用命令格式如下:

// 命令行輸出類名虎谢、類數(shù)量數(shù)量,類占用內(nèi)存大小曹质, 
// 按照類占用內(nèi)存大小降序排列 
jmap -histo <pid> 

// 生成堆內(nèi)存轉(zhuǎn)儲(chǔ)快照婴噩,在當(dāng)前目錄下導(dǎo)出dump.hrpof的二進(jìn)制文件, 
// 可以用eclipse的MAT圖形化工具分析 
jmap -dump:live,format=b,file=dump.hprof <pid> 

jinfo羽德,命令格式:

jinfo <pid> 

用來查看正在運(yùn)行的 Java 應(yīng)用程序的擴(kuò)展參數(shù)几莽,包括 Java System 屬性和 JVM 命令行參數(shù)。

其他 GC 工具:

  • 監(jiān)控告警系統(tǒng):Zabbix宅静、Prometheus章蚣、Open-Falcon
  • jdk 自動(dòng)實(shí)時(shí)內(nèi)存監(jiān)控工具:VisualVM
  • 堆外內(nèi)存監(jiān)控:Java VisualVM 安裝 Buffer Pools 插件、google perf工具、Java NMT(Native Memory Tracking)工具
  • GC 日志分析:GCViewer纤垂、gceasy
  • GC 參數(shù)檢查和優(yōu)化:http://xxfox.perfma.com/

GC 優(yōu)化案例

①數(shù)據(jù)分析平臺(tái)系統(tǒng)頻繁 Full GC

平臺(tái)主要對(duì)用戶在 App 中行為進(jìn)行定時(shí)分析統(tǒng)計(jì)矾策,并支持報(bào)表導(dǎo)出,使用 CMS GC 算法峭沦。

數(shù)據(jù)分析師在使用中發(fā)現(xiàn)系統(tǒng)頁面打開經(jīng)臣炙洌卡頓,通過 jstat 命令發(fā)現(xiàn)系統(tǒng)每次 Young GC 后大約有 10% 的存活對(duì)象進(jìn)入老年代吼鱼。

原來是因?yàn)?Survivor 區(qū)空間設(shè)置過小蓬豁,每次 Young GC 后存活對(duì)象在 Survivor 區(qū)域放不下,提前進(jìn)入老年代菇肃。

通過調(diào)大 Survivor 區(qū)地粪,使得 Survivor 區(qū)可以容納 Young GC 后存活對(duì)象,對(duì)象在 Survivor 區(qū)經(jīng)歷多次 Young GC 達(dá)到年齡閾值才進(jìn)入老年代琐谤。

調(diào)整之后每次 Young GC 后進(jìn)入老年代的存活對(duì)象穩(wěn)定運(yùn)行時(shí)僅幾百 Kb蟆技,F(xiàn)ull GC 頻率大大降低。

②業(yè)務(wù)對(duì)接網(wǎng)關(guān) OOM

網(wǎng)關(guān)主要消費(fèi) Kafka 數(shù)據(jù)斗忌,進(jìn)行數(shù)據(jù)處理計(jì)算然后轉(zhuǎn)發(fā)到另外的 Kafka 隊(duì)列质礼,系統(tǒng)運(yùn)行幾個(gè)小時(shí)候出現(xiàn) OOM,重啟系統(tǒng)幾個(gè)小時(shí)之后又 OOM飞蹂。

通過 jmap 導(dǎo)出堆內(nèi)存,在 eclipse MAT 工具分析才找出原因:代碼中將某個(gè)業(yè)務(wù) Kafka 的 topic 數(shù)據(jù)進(jìn)行日志異步打印翻屈,該業(yè)務(wù)數(shù)據(jù)量較大陈哑,大量對(duì)象堆積在內(nèi)存中等待被打印,導(dǎo)致 OOM伸眶。

③賬號(hào)權(quán)限管理系統(tǒng)頻繁長(zhǎng)時(shí)間 Full GC

系統(tǒng)對(duì)外提供各種賬號(hào)鑒權(quán)服務(wù)惊窖,使用時(shí)發(fā)現(xiàn)系統(tǒng)經(jīng)常服務(wù)不可用,通過 Zabbix 的監(jiān)控平臺(tái)監(jiān)控發(fā)現(xiàn)系統(tǒng)頻繁發(fā)生長(zhǎng)時(shí)間 Full GC厘贼,且觸發(fā)時(shí)老年代的堆內(nèi)存通常并沒有占滿界酒,發(fā)現(xiàn)原來是業(yè)務(wù)代碼中調(diào)用了 System.gc()。

總結(jié)

GC 問題可以說沒有捷徑嘴秸,排查線上的性能問題本身就并不簡(jiǎn)單毁欣,除了將本文介紹到的原理和工具融會(huì)貫通,還需要我們不斷去積累經(jīng)驗(yàn)岳掐,真正做到性能最優(yōu)凭疮。

篇幅所限,不再展開介紹常見 GC 參數(shù)的使用串述,我發(fā)布在 GitHub:https://github.com/caison/caison-blog-demo

參考:

  • 《Java Performance: The Definitive Guide》 Scott Oaks
  • 《深入理解 Java 虛擬機(jī):JVM 高級(jí)特性與最佳實(shí)踐(第二版》 周志華
  • Java 性能調(diào)優(yōu)實(shí)戰(zhàn)
  • Getting Started with the G1 Garbage Collector
  • GC 參考手冊(cè)-Java 版
  • 請(qǐng)教 G1 算法的原理——RednaxelaFX 的回答
  • Java Hotspot G1 GC 的一些關(guān)鍵技術(shù)——美團(tuán)技術(shù)團(tuán)隊(duì)

轉(zhuǎn)自:https://www.toutiao.com/i6740803737603801604/?tt_from=mobile_qq&utm_campaign=client_share&timestamp=1589932774&app=news_article&utm_source=mobile_qq&utm_medium=toutiao_android&use_new_style=1&req_id=20200520075934010023028157021CE003&group_id=6740803737603801604

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末执解,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纲酗,更是在濱河造成了極大的恐慌衰腌,老刑警劉巖新蟆,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異右蕊,居然都是意外死亡琼稻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門尤泽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來欣簇,“玉大人,你說我怎么就攤上這事坯约⌒苎剩” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵闹丐,是天一觀的道長(zhǎng)横殴。 經(jīng)常有香客問我,道長(zhǎng)卿拴,這世上最難降的妖魔是什么衫仑? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮堕花,結(jié)果婚禮上文狱,老公的妹妹穿的比我還像新娘。我一直安慰自己缘挽,他們只是感情好瞄崇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著壕曼,像睡著了一般苏研。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上腮郊,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天摹蘑,我揣著相機(jī)與錄音,去河邊找鬼轧飞。 笑死衅鹿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的过咬。 我是一名探鬼主播塘安,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼援奢!你這毒婦竟也來了兼犯?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎切黔,沒想到半個(gè)月后砸脊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡纬霞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年凌埂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诗芜。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡瞳抓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出伏恐,到底是詐尸還是另有隱情孩哑,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布翠桦,位于F島的核電站横蜒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏销凑。R本人自食惡果不足惜丛晌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望斗幼。 院中可真熱鬧澎蛛,春花似錦、人聲如沸蜕窿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽渠羞。三九已至斤贰,卻和暖如春智哀,著一層夾襖步出監(jiān)牢的瞬間次询,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工瓷叫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屯吊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓摹菠,卻偏偏與公主長(zhǎng)得像盒卸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子次氨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354