Java之CMS和G1垃圾回收過(guò)程的異同說(shuō)明
CMS 和 G1 垃圾回收過(guò)程的異同
垃圾回收
CMS 垃圾回收
CMS 垃圾收集器是基于并發(fā)-清理(Mark-Sweep) 算法,以獲取最短停頓時(shí)間為目標(biāo)的垃圾收集器,是 JVM 第一款真正意義上的并發(fā)收集器。CMS 垃圾回收整體分為 4 個(gè)過(guò)程
初始標(biāo)記(CMS initial mark)
- STW, 暫停所有業(yè)務(wù)線程
- 標(biāo)記 GC Roots 的直接關(guān)聯(lián)對(duì)象
- 能找到的第一個(gè)對(duì)象,速度很快
并發(fā)標(biāo)記 (CMS concurrent mark)
- 并發(fā)標(biāo)記是基于初始標(biāo)記得知的 GC Roots 的直接關(guān)聯(lián)對(duì)象開(kāi)始遍歷整個(gè)對(duì)象圖的過(guò)程,這個(gè)過(guò)程耗時(shí)較長(zhǎng),但無(wú)需 STW, GC 線程與業(yè)務(wù)線程同時(shí)進(jìn)行,會(huì)造成浮動(dòng)垃圾和漏標(biāo)問(wèn)題
- 過(guò)程中,GC 線程會(huì)記錄黑色對(duì)象引用白色對(duì)象的記錄,并用于重新標(biāo)記階段的二次檢查
重新標(biāo)記 (CMS remark)
- 基于并發(fā)標(biāo)記產(chǎn)生的漏標(biāo)問(wèn)題,開(kāi)啟 STW, 使用增量更新算法,將并發(fā)標(biāo)記期間出現(xiàn)的漏標(biāo)現(xiàn)象進(jìn)行二次標(biāo)記。即黑色對(duì)象被置為灰色,GC 線程重新掃描這些對(duì)象的引用路徑,避免漏標(biāo)
- 這個(gè)階段的停頓會(huì)比初始標(biāo)記要長(zhǎng)
并發(fā)清理 (CMS concurrent sweep)
- GC 線程開(kāi)始對(duì)白色對(duì)象
- 即無(wú)引用的垃圾進(jìn)行回收
CMS 會(huì)造成的問(wèn)題
- 多標(biāo)問(wèn)題,產(chǎn)生浮動(dòng)垃圾
- 漏標(biāo)問(wèn)題, 產(chǎn)生 BUG
G1 垃圾回收
G1 垃圾收集器整體上采用標(biāo)記-整理算法,細(xì)節(jié)上是一種復(fù)制算法,作為一種全功能全代的垃圾收集器,是 JDK9 之后的默認(rèn)垃圾收集器,用于替代 CMS。垃圾回收的主要過(guò)程跟 CMS 一樣,主要也是 4 個(gè)階段
初始標(biāo)記 (Inital Marking)
- STW, 僅標(biāo)記 GC Roots 能直接關(guān)聯(lián)到的對(duì)象,耗時(shí)很短,跟 CMS 一樣
并發(fā)標(biāo)記 (Concurrent Marking)
- 從 GC Roots 直接關(guān)聯(lián)對(duì)象開(kāi)始可達(dá)性分析,掃描對(duì)象圖,耗時(shí)很長(zhǎng),并發(fā)執(zhí)行。
- 同理會(huì)造成浮動(dòng)垃圾和漏標(biāo)問(wèn)題;為了避免漏標(biāo)問(wèn)題,采用 SATB 原始快照記錄下在并發(fā)時(shí)有引用變動(dòng)的對(duì)象。即白色對(duì)象從灰色對(duì)象的引用中斷開(kāi)
最終標(biāo)記 (Final Marking)
- STW, 用于處理并發(fā)標(biāo)記階段留下的少量 SATB 記錄。
- 將斷開(kāi)的白色對(duì)象置為灰色,讓 GC 線程重新掃描這些對(duì)象
篩選回收 (Live Data Counting And Evacuation)
- 負(fù)責(zé)更新 Region 的統(tǒng)計(jì)數(shù)據(jù),對(duì)各個(gè) Region 的回收價(jià)值和成本進(jìn)行排序。然后根據(jù)用戶預(yù)期停頓時(shí)間,自由任意選擇多個(gè) Region 構(gòu)成回收集(CSet)
- STW, GC 線程并行將需要回收的 CSet 中的存活對(duì)象復(fù)制到空閑狀態(tài)的 Region 中
注意解釋
- 這里對(duì)篩選回收其實(shí)是一種通用簡(jiǎn)單的解釋方案,實(shí)際上并不是這樣;老年代回收分為兩個(gè)階段標(biāo)記,一,并發(fā)標(biāo)記階段。二,垃圾回收階段。而并發(fā)標(biāo)記階段分為 4 個(gè)階段(初始標(biāo)記,并發(fā)標(biāo)記,重標(biāo)記,清理階段)。本文將并發(fā)標(biāo)記階段的清理子階段和垃圾回收階段合并稱之為篩選回收
- 實(shí)際上清理子階段并不處理垃圾回收的事情,僅僅統(tǒng)計(jì) region 排序,得出 Cset, 不過(guò)依然要 STW; 而垃圾回收階段才真正進(jìn)行存活對(duì)象的拷貝和垃圾回收。但每次垃圾回收都是在新的一輪 YGC 開(kāi)始才會(huì)發(fā)生。即不管是 YGC 還是 Mixed GC,最終只會(huì)得到要清理的 CSet, 在新的一輪 CG 來(lái)臨時(shí),才會(huì)去清理,即延遲清理。雖然我們不知道有什么好處
CMS, G1 針對(duì)三色標(biāo)記漏標(biāo)的策略
不管是 CMS 還是 G1, 都是采用三色標(biāo)記算法來(lái)處理并發(fā)垃圾回收的問(wèn)題,但對(duì)其可能會(huì)造成的漏標(biāo)行為,卻采用了不同的行為
增量更新策略
- 在并發(fā)標(biāo)記階段,記錄由黑色對(duì)象引用白色對(duì)象的變動(dòng)
- 在重新標(biāo)記階段,STW, 對(duì)黑色對(duì)象置灰,重新掃描灰色對(duì)象以及其引用路徑
我的理解中,增量更新策略就類似在黑色對(duì)象引用白色對(duì)象的插入屏障中,記錄這樣一個(gè)信息,交給重新標(biāo)記階段去處理
SATB (snapshot-at-beginning)策略
- 在并發(fā)標(biāo)記階段,記錄從灰色對(duì)象中斷開(kāi)的白色對(duì)象的變動(dòng),說(shuō)白了就是將原有引用信息記錄下來(lái),后續(xù)處理
- 在最終標(biāo)記階段,STW 處理這些變化,將斷開(kāi)的白色對(duì)象置為灰色,最終等待 GC 線程掃描
我的理解是,SATB 同樣是利用寫(xiě)屏障,只不過(guò)利用的是刪除屏障,即在白色對(duì)象斷開(kāi)的時(shí)候,記錄下原有的指向信息,因?yàn)樗J(rèn)為在一開(kāi)始所記錄的所有對(duì)象都要被掃描。同理交給最終標(biāo)記階段去處理
Golang 策略
- 插入屏障/ 刪除屏障
- 混合寫(xiě)屏障
G1 有什么優(yōu)勢(shì)?為什么可以替換 CMS ?
G1 的優(yōu)勢(shì)
- 具有可預(yù)測(cè)的停頓時(shí)間模型,支持用戶設(shè)置預(yù)期的停頓時(shí)長(zhǎng);即 G1 可以盡可能讓停頓時(shí)間在用戶希望的范圍浮動(dòng)
- 全新設(shè)計(jì),全年代回收,簡(jiǎn)單高效
- 相較 CMS ,不會(huì)產(chǎn)生空間碎片
G1 的缺點(diǎn)
- 相較 CMS 需要更多的系統(tǒng)內(nèi)存占用,比如占用總空間的 10 ~ 20 %
替換場(chǎng)景
- 在 CMS 上,假設(shè)在秒殺場(chǎng)景,一瞬間來(lái)了大量請(qǐng)求,如果年輕代設(shè)置的過(guò)小,會(huì)導(dǎo)致 Survice 區(qū)因?yàn)橛袑?duì)象存活,導(dǎo)致新對(duì)象無(wú)法進(jìn)入年輕代,而被擔(dān)保進(jìn)老年代。逐漸積累,會(huì)導(dǎo)致 Full GC。尤其在高并發(fā)場(chǎng)景,這非常容易導(dǎo)致頻繁的 Full GC, 這也就會(huì)造成嚴(yán)重的卡頓
- 在 CMS 上,解決方式也很簡(jiǎn)單,因?yàn)槊霘⑺查g,大部分對(duì)象都是一會(huì)就死亡的,基本不會(huì)是老年代對(duì)象。所以我們只需要縮小老年代大小,增加年輕代大小就可以解決這個(gè)問(wèn)題
- 在 G1 上,就更容易解決了, 因?yàn)槟贻p代大小會(huì)根據(jù)每次的 YGC 進(jìn)行自動(dòng)的調(diào)節(jié)。我們不需要主動(dòng)設(shè)置,G1 可以自行的解決這個(gè)問(wèn)題,自行選擇合適的年輕代和老年代比例
問(wèn)題
CMS 初始標(biāo)記為什么需要 STW ?
- 如果初始標(biāo)記不 STW , 在程序運(yùn)行的過(guò)程中,會(huì)有不斷的局部變量出現(xiàn),這樣我們就不知道要標(biāo)記到什么時(shí)候才算結(jié)束了
- 同時(shí)初始標(biāo)記耗時(shí)很短,所以即時(shí) STW 也不會(huì)影響什么
G1 篩選回收為什么需要 STW ?
- 首先篩選回收階段,我們需要分為兩個(gè)部分,一部分是進(jìn)行 Region 垃圾信息等統(tǒng)計(jì),并根據(jù)排序選擇指定的 CSet, 這部分需要 STW, 避免并發(fā)帶來(lái)的麻煩。
- 另一部分就是垃圾清理, 因?yàn)樯婕暗搅舜婊顚?duì)象的復(fù)制,將對(duì)象從一塊區(qū)域復(fù)制到另一塊區(qū)域,如果沒(méi)有 STW, 可能會(huì)導(dǎo)致對(duì)象覆蓋的問(wèn)題
在并發(fā)標(biāo)記階段,如果沒(méi)有 STW, 新加入的對(duì)象要怎么處理?
新加入的對(duì)象有那么幾種可能的情況
- 新加入對(duì)象是GC Roots 直接關(guān)聯(lián)對(duì)象
- 新加入對(duì)象被黑色對(duì)象引用了
- 新加入對(duì)象被白色或灰色對(duì)象引用了
假設(shè)新加入對(duì)象分配為白色對(duì)象,那么就存在幾個(gè)問(wèn)題
- 如果是 GC Roots 直接關(guān)聯(lián)對(duì)象,因?yàn)橐呀?jīng)錯(cuò)過(guò)了初始標(biāo)記,所以會(huì)導(dǎo)致新對(duì)象被認(rèn)為是垃圾,而被回收掉
- 如果被黑色對(duì)象引用,且沒(méi)有其他引用路徑,這就造成了漏標(biāo)問(wèn)題,依舊會(huì)被當(dāng)做是垃圾回收掉
- 如果是白色或灰色引用,那么問(wèn)題不大
不管怎么說(shuō),依然會(huì)存在新加入對(duì)象被當(dāng)做垃圾回收掉的問(wèn)題,所以新對(duì)象直接分配為白色對(duì)象是有問(wèn)題的,我們要怎么處理才合理呢?
在不同的語(yǔ)言,不同的垃圾收集器,我猜測(cè)應(yīng)該由不同的處理方式,這里可以列舉一下
- Go 1.8 中,三色標(biāo)記 + 混合寫(xiě)屏障策略下,新加入的對(duì)象都標(biāo)記為灰色。所以新對(duì)象肯定會(huì)被 GC 線程掃描。優(yōu)點(diǎn)就是避免漏標(biāo),缺點(diǎn)就是可能多標(biāo),造成浮動(dòng)垃圾
- 同理,CMS 或 G1 我猜測(cè)也是使用了類似方案
G1 到底使用什么垃圾回收算法?
網(wǎng)上通常說(shuō) G1 采用的是標(biāo)記-整理算法,整體上也可以這么去理解
- 整體標(biāo)記-整理,細(xì)節(jié)復(fù)制算法
- G1 會(huì)將需要回收的 region 中活躍的對(duì)象拷貝到空閑分區(qū),然后將回收 region 進(jìn)行清理,再歸類為空閑分區(qū),這一個(gè)過(guò)程像是標(biāo)記整理,也像復(fù)制算法
- G1 只提供有 YGC 和 Mixed GC 選項(xiàng), 如果Mixed GC實(shí)在無(wú)法跟上程序分配內(nèi)存的速度,導(dǎo)致老年代填滿無(wú)法繼續(xù)進(jìn)行 Mixed GC,就會(huì)使用Serial Old GC(Full GC)來(lái)收集整個(gè)GC heap。所以我們可以知道,G1是不提供Full GC的, 且要避免 Full GC
- JDK10 之間只有串行 Full GC, 11 后則由并行 Full GC 了
G1 GC 與垃圾回收過(guò)程怎么混合理解?
G1 的并發(fā)標(biāo)記階段僅僅針對(duì)與老年代對(duì)象的分析,即我們?cè)谏厦婷枋龅睦厥针A段是針對(duì) G1 老年代回收的,新生代回收相比以上更加的簡(jiǎn)單。全程 STW, GC 線程并行回收新生代對(duì)象
實(shí)際上 Mixed GC 就包含了 YGC 的整個(gè)過(guò)程,即如果發(fā)生了 Mixed GC, 那么之前必然發(fā)生了 YGC。我們可以簡(jiǎn)單的理解 YGC 的過(guò)程只有兩個(gè)階段 (初始標(biāo)記,篩選回收)。而在初始標(biāo)記過(guò)后,當(dāng)堆內(nèi)存的占用到達(dá)內(nèi)存可用總量的 45% (可設(shè)置),就會(huì)觸發(fā)并發(fā)標(biāo)記階, 此時(shí)就代表從 YGC 進(jìn)階到 Mixed GC, 之后就走正常的流程了
Mixed GC 的開(kāi)始,基于 G1 YGC 的完成,并發(fā)標(biāo)記過(guò)程由并發(fā)標(biāo)記線程從 Survivor 區(qū)或老年代的 RSet 記錄的活躍對(duì)象作為根并開(kāi)始標(biāo)記
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
MyBatis?超詳細(xì)講解動(dòng)態(tài)SQL的實(shí)現(xiàn)
動(dòng)態(tài)?SQL?是?MyBatis?的強(qiáng)大特性之一。如果你使用過(guò)?JDBC?或其它類似的框架,你應(yīng)該能理解根據(jù)不同條件拼接?SQL?語(yǔ)句有多痛苦,例如拼接時(shí)要確保不能忘記添加必要的空格,還要注意去掉列表最后一個(gè)列名的逗號(hào)。利用動(dòng)態(tài)?SQL,可以徹底擺脫這種痛苦2022-03-03Java實(shí)現(xiàn)簡(jiǎn)單點(diǎn)餐系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單點(diǎn)餐系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01java抓取網(wǎng)頁(yè)數(shù)據(jù)獲取網(wǎng)頁(yè)中所有的鏈接實(shí)例分享
java抓取網(wǎng)頁(yè)數(shù)據(jù)獲取網(wǎng)頁(yè)中所有的鏈接實(shí)例分享,使用方法,只要實(shí)例化HtmlParser時(shí)傳入網(wǎng)頁(yè)地址就可以了2013-12-12解析MapStruct轉(zhuǎn)換javaBean時(shí)出現(xiàn)的詭異事件
在項(xiàng)目中用到了MapStruct,對(duì)其可以轉(zhuǎn)換JavaBean特別好奇,今天小編給大家分享一個(gè)demo給大家講解MapStruct轉(zhuǎn)換javaBean時(shí)出現(xiàn)的詭異事件,感興趣的朋友一起看看吧2021-09-09Java開(kāi)發(fā)環(huán)境jdk 1.8安裝配置方法(Win7 64位系統(tǒng)/windows server 2008)
這篇文章主要介紹了Java開(kāi)發(fā)環(huán)境配置方法(Win7 64位系統(tǒng)/windows server 2008),需要的朋友可以參考下2016-10-10