正文
本文的內(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 問題排查和解決思路
1. GC 基礎(chǔ)原理
1.1. 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)吞吐量解孙。
1.2. GC 分代收集算法
現(xiàn)代的垃圾收集器基本都是采用分代收集算法抛人,其主要思想: 將 Java 的堆內(nèi)存邏輯上分成兩塊:新生代、老年代廷臼,針對(duì)不同存活周期盅惜、不同大小的對(duì)象采取不同的垃圾回收策略忌穿。
1.2.1. 新生代(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)移到老年代。
1.2.2. 老年代(Old Generation)
在新生代中經(jīng)歷了 N 次垃圾回收后仍然存活的對(duì)象梅肤,就會(huì)被放到老年代姨蝴,該區(qū)域中對(duì)象存活率高。老年代的垃圾回收通常使用“標(biāo)記-整理”算法吨些。
1.3. GC 事件分類
根據(jù)垃圾收集回收的區(qū)域不同炒辉,垃圾收集主要分為:
Young GC
Old GC
Full GC
Mixed GC
1.3.1. 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ì)。
1.3.2. Old GC/Full GC/Mixed GC
Old GC:只清理老年代空間的 GC 事件,只有 CMS 的并發(fā)收集是這個(gè)模式昂灵。
Full GC:清理整個(gè)堆的 GC 事件舞萄,包括新生代、老年代撑螺、元空間等 把还。
Mixed GC:清理整個(gè)新生代以及部分老年代的 GC茸俭,只有 G1 有這個(gè)模式调鬓。
1.4. 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復(fù)制代碼
常見的 Young GC唇敞、Full GC 日志含義如下:
Young GC
Full GC
免費(fèi)的 GC 日志圖形分析工具推薦下面 2 個(gè):
GCViewer:下載 jar 包直接運(yùn)行
gceasy:Web 工具疆柔,上傳 GC 日志在線使用
1.5. 內(nèi)存分配策略
Java 提供的自動(dòng)內(nèi)存管理镶柱,可以歸結(jié)為解決了對(duì)象的內(nèi)存分配和回收的問題。前面已經(jīng)介紹了內(nèi)存回收鞋屈,下面介紹幾條最普遍的內(nèi)存分配策略:
1.5.1. 對(duì)象優(yōu)先在 Eden 區(qū)分配
大多數(shù)情況下,對(duì)象在先新生代 Eden 區(qū)中分配湖蜕。當(dāng) Eden 區(qū)沒有足夠空間進(jìn)行分配時(shí)宋列,虛擬機(jī)將發(fā)起一次 Young GC。
1.5.2. 大對(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ù)制黔衡。
1.5.3. 長(zhǎng)期存活的對(duì)象進(jìn)入老年代
對(duì)象每經(jīng)歷一次垃圾回收,且沒被回收掉夜牡,它的年齡就增加 1侣签,大于年齡閾值參數(shù)(-XX:MaxTenuringThreshold,默認(rèn) 15)的對(duì)象蹦肴,將晉升到老年代中猴娩。
1.5.4. 空間分配擔(dān)保
當(dāng)進(jìn)行 Young GC 之前,JVM 需要預(yù)估:老年代是否能夠容納 Young GC 后新生代晉升到老年代的存活對(duì)象胀溺,以確定是否需要提前觸發(fā) GC 回收老年代空間,基于空間分配擔(dān)保策略來計(jì)算背零。
Young GC 之后如果成功(Young GC 后晉升對(duì)象能放入老年代)无埃,則代表擔(dān)保成功,不用再進(jìn)行 Full GC侦镇,提高性能壳繁。
如果失敗,則會(huì)出現(xiàn)“promotion failed”錯(cuò)誤闹炉,代表擔(dān)保失敗渣触,需要進(jìn)行 Full GC。
1.5.5. 動(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)入老年代刻撒,需要盡量避免。
2. CMS 原理及調(diào)優(yōu)
2.1. 術(shù)語解釋
2.1.1. 可達(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ì)象不再存活。
2.1.2. Stop The World
GC 過程中分析對(duì)象引用關(guān)系蛉签,為了保證分析結(jié)果的準(zhǔn)確性碍舍,需要通過停頓所有 Java 執(zhí)行線程,保證引用關(guān)系不再動(dòng)態(tài)變化妈经,該停頓事件稱為 Stop The World(STW)捧书。
2.1.3. Safepoint
代碼執(zhí)行過程中的一些特殊位置,當(dāng)線程執(zhí)行到這些位置的時(shí)候爆哑,說明虛擬機(jī)當(dāng)前的狀態(tài)是安全的舆吮,如果有需要 GC,線程可以在這個(gè)位置暫停潭袱。
HotSpot 采用主動(dòng)中斷的方式锋恬,讓執(zhí)行線程在運(yùn)行期輪詢是否需要暫停的標(biāo)志,若需要?jiǎng)t中斷掛起趟径。
2.2. 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復(fù)制代碼
值得補(bǔ)充的是拟淮,下面介紹到的 CMS GC 是指老年代的 GC谴忧,而 Full GC 指的是整個(gè)堆的 GC 事件,包括新生代委造、老年代搏屑、元空間等粉楚,兩者有所區(qū)分模软。
2.3. 新生代垃圾回收
能與 CMS 搭配使用的新生代垃圾收集器有 Serial 收集器和 ParNew 收集器。
這 2 個(gè)收集器都采用標(biāo)記復(fù)制算法携狭,都會(huì)觸發(fā) STW 事件回俐,停止所有的應(yīng)用線程稀并。不同之處在于碘举,Serial 是單線程執(zhí)行搁廓,ParNew 是多線程執(zhí)行。
作者:零壹技術(shù)棧
鏈接:https://juejin.cn/post/6844903953415536654
來源:稀土掘金
著作權(quán)歸作者所有蝙场。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)粱年,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處台诗。