再回首CMS垃圾回收

前言

之前學(xué)習(xí)JVM垃圾回收時(shí),主要是過了一遍垃圾收集算法野瘦,比如復(fù)制算法描沟,標(biāo)記-清除算法,標(biāo)記-整理算法鞭光,在此基礎(chǔ)上可以增加分代吏廉,每代采取不同的回收算法,以提高整體的分配和回收效率惰许。然后過了一遍JVM中的垃圾收集器席覆,比如Serial、Parallel Scavenge汹买、Parallel New佩伤、CMS聊倔、G1等。

自認(rèn)為垃圾收集就是根據(jù)GC Root標(biāo)記所有可達(dá)的對(duì)象生巡,然后把所有沒有標(biāo)記的對(duì)象清除就ok了耙蔑。是不是很簡(jiǎn)單。事實(shí)上垃圾收集也就是這么一回事孤荣,但是很多時(shí)候說起來簡(jiǎn)單甸陌,做起來卻會(huì)出現(xiàn)很多問題。這篇文章就是記錄我對(duì)CMS垃圾收集器的一些疑問并學(xué)習(xí)的過程垃环。

首先看一下CMS的整體流程(具體每個(gè)流程的詳情就自行了解吧)

CMS流程

如何進(jìn)行標(biāo)記邀层?

最近在看Golang的GC算法實(shí)現(xiàn),里面用到了三色標(biāo)記法遂庄,但是在我的知識(shí)庫中對(duì)三色標(biāo)記法有這個(gè)概念寥院,是的,我只知道這個(gè)概念涛目,不知道三色標(biāo)記法是怎么一個(gè)流程秸谢,也不知道三色標(biāo)記法在GC中怎么與運(yùn)行的。于是就開始了我的探險(xiǎn)之旅霹肝。

在搜索了一下三色標(biāo)記法(具體可以看一下文末參考文檔中三色標(biāo)記法與讀寫屏障了解詳情)后估蹄,發(fā)現(xiàn)現(xiàn)代追蹤式(可達(dá)性分析)的垃圾回收器幾乎都借鑒了三色標(biāo)記的算法思想,CMS垃圾收集器也不例外沫换。

GC Root有哪些臭蚁?

我們知道怎么進(jìn)行標(biāo)記了,但最初標(biāo)記的時(shí)候需要一些根據(jù)才行啊讯赏,這些根據(jù)就是我們說的GC Root垮兑。GC Root有哪些?網(wǎng)上有很多的答案漱挎,我的理解就是

  • 當(dāng)前活躍調(diào)用棧中的指向?qū)ο蟮囊?/li>
  • 一些不會(huì)發(fā)生改變的數(shù)據(jù)所指向的引用

這里我使用的是引用系枪,而不是對(duì)象,因?yàn)镽大是這樣說的(具體的問題見參考文檔java的gc為什么要分代磕谅?

所謂“GC roots”私爷,或者說tracing GC的“根集合”,就是一組必須活躍的引用膊夹。
例如說衬浑,這些引用可能包括:

  • 所有Java線程當(dāng)前活躍的棧幀里指向GC堆里的對(duì)象的引用;換句話說放刨,當(dāng)前所有正在被調(diào)用的方法的引用類型的參數(shù)/局部變量/臨時(shí)值嚎卫。
  • VM的一些靜態(tài)數(shù)據(jù)結(jié)構(gòu)里指向GC堆里的對(duì)象的引用,例如說HotSpot VM里的Universe里有很多這樣的引用。
  • JNI handles拓诸,包括global handles和local handles
  • (看情況)所有當(dāng)前被加載的Java類
  • (看情況)Java類的引用類型靜態(tài)變量
  • (看情況)Java類的運(yùn)行時(shí)常量池里的引用類型常量(String或Class類型)
  • (看情況)String常量池(StringTable)里的引用

注意,是一組必須活躍的引用麻昼,不是對(duì)象奠支。

現(xiàn)在知道了GC Root,但是我們都知道有分代的概念抚芦,新生代的gc和老年的代的gc回收的區(qū)域是不一樣倍谜,那么這里的GC Root是不是應(yīng)該不一樣呢?肯定是不一樣的叉抡。

首先看一下新生代的GC

新生代的區(qū)域一般都比較小尔崔,而且對(duì)象的存活率都比較低,所以按照前面說的GC Root在新生代的區(qū)域掃描就行了褥民。但是會(huì)有一個(gè)問題季春?老年代存在引用新生代對(duì)象的可能啊消返?如果只掃描新生代的區(qū)域载弄,會(huì)漏掉被老年代引用的對(duì)象,這些對(duì)象就會(huì)被清除掉撵颊,這是不允許的宇攻。

如果這樣的話,那是不是掃描一下老年代的對(duì)象倡勇,看是否引用新生代的對(duì)象是不是就ok了逞刷?嗯這么做肯定是ok的,但是老年代一般很大妻熊,而且存活的對(duì)象很多夸浅,會(huì)導(dǎo)致掃描占用很長(zhǎng)的時(shí)間。那這個(gè)問題如何解固耘?JVM是如何避免Minor GC時(shí)掃描全堆的题篷?

經(jīng)過統(tǒng)計(jì)信息顯示,老年代持有新生代對(duì)象引用的情況不足1%厅目,根據(jù)這一特性JVM引入了卡表(card table)來實(shí)現(xiàn)這一目的番枚。如下圖所示:

CardTable

卡表的具體策略是將老年代的空間分成大小為512B的若干張卡(card)。卡表本身是單字節(jié)數(shù)組,數(shù)組中的每個(gè)元素對(duì)應(yīng)著一張卡废麻,當(dāng)發(fā)生老年代引用新生代時(shí)甘苍,虛擬機(jī)將該卡對(duì)應(yīng)的卡表元素設(shè)置為適當(dāng)?shù)闹怠H缟蠄D所示并蝗,卡表3被標(biāo)記為臟(卡表還有另外的作用,標(biāo)識(shí)并發(fā)標(biāo)記階段哪些塊被修改過)帘瞭,之后Minor GC時(shí)通過掃描卡表就可以很快的識(shí)別哪些卡中存在老年代指向新生代的引用洋丐。這樣虛擬機(jī)通過空間換時(shí)間的方式呈昔,避免了全堆掃描。

所以新年代GC的GC Root包含2部分

  • 新生代中滿足GC Root定義的對(duì)象
  • 卡表中老年代引用新生代的對(duì)象

老年代的GC

前面我們說了新生代的gc友绝,我們已同樣的思路來看看老年代的gc堤尾,老年代的GC Root如何來標(biāo)記呢?只掃描老年代可以嗎迁客?當(dāng)然是不行的郭宝,因?yàn)樾律幸部赡艽嬖诶夏甏鷮?duì)象的引用,好在新生代并不大掷漱,所以老年代GC的時(shí)候還需要掃描一遍新生代粘室。

新生代GC的Root

所以老年代GC的GC Root包含2部分

  • 老生代中滿足GC Root定義的對(duì)象,如圖節(jié)點(diǎn)1卜范;
  • 標(biāo)記年輕代中活著的對(duì)象引用到的老年代的對(duì)象(指的是年輕代中還存活的引用類型對(duì)象衔统,引用指向老年代中的對(duì)象)如圖節(jié)點(diǎn)2、3先朦;

并發(fā)標(biāo)記的好壞?

標(biāo)記作為垃圾回收的第一步缰冤,現(xiàn)在知道如何進(jìn)行標(biāo)記,接下來就是遍歷這些對(duì)象喳魏,將所有未標(biāo)記的對(duì)象清理就完成GC了棉浸。

然而事實(shí)上并沒有這么簡(jiǎn)單,如果標(biāo)記的時(shí)候是STW的刺彩,那就是這么簡(jiǎn)單迷郑,但是如果標(biāo)記過程都STW會(huì)造成暫停時(shí)間過長(zhǎng),給人的感覺就是系統(tǒng)一卡一卡的创倔。

于是就把標(biāo)記的過程改成并發(fā)的進(jìn)行嗡害,也就是CMS中并發(fā)標(biāo)記的過程,然而這就是一切復(fù)雜問題的源頭畦攘。雖然并發(fā)標(biāo)記提升了標(biāo)記的效率霸妹,但是因此卻引發(fā)了一系列的問題。

因?yàn)椴l(fā)標(biāo)記時(shí)知押,gc線程和用戶線程是并行的叹螟,所以在這個(gè)過程中會(huì)出現(xiàn)下面的情況(需要了解三色標(biāo)記法與讀寫屏障):

  • 新生代晉升到老年代
  • 黑色對(duì)象取消對(duì)灰色對(duì)象的引用(浮動(dòng)垃圾)
  • 黑色對(duì)象新增對(duì)白色對(duì)象的引用(漏標(biāo))

其實(shí)在三色標(biāo)記法與讀寫屏障文中已經(jīng)給出了解決方法--添加讀寫屏障

  • 寫屏障 + SATB
  • 寫屏障 + 增量更新
  • 讀屏障(Load Barrier)

在CMS并發(fā)標(biāo)記階段,使用 寫屏障 + 增量更新 的方法台盯,將上面出現(xiàn)的情況標(biāo)記為dirty罢绽,這樣最后再遍歷處理一下Dirty集合中的對(duì)象就ok了

標(biāo)記為dirty

重新標(biāo)記階段為什么還要掃描新生代?

因?yàn)榇嬖?strong>跨代引用静盅,但是前面說過這種情況良价,通過讀寫屏障的方式標(biāo)記這些為dirty,只需要掃描老年代和dirty集合就行了啊明垢?哎蚣常,看來我還是太年輕,如果只掃描老年代和dirty集合會(huì)漏掉一部分袖外,會(huì)是哪部分呢史隆?老年代和dirty集合還沒有覆蓋完嗎?

是的曼验,老年代和dirty集合的確沒有覆蓋完。我們來分析一下粘姜。老年代中經(jīng)過初始標(biāo)記和并發(fā)標(biāo)記后鬓照,只有黑色對(duì)象和白色對(duì)象了,黑色的就是要留下的孤紧,白色的就是要被清除的豺裆。黑色對(duì)象是怎么來的?根據(jù)GC Root找到的号显,所以只要并發(fā)標(biāo)記過程中臭猜,GC Root不發(fā)生變化,黑色對(duì)象就沒有問題(不會(huì)漏標(biāo))押蚤,如果在并發(fā)標(biāo)記過程中GC Root發(fā)生了變化呢蔑歌?

當(dāng)并發(fā)標(biāo)記過程中GC Root增加了,并且這個(gè)GC Root還引用了老年代中的對(duì)象揽碘,此時(shí)如果只掃描老年代和dirty集合就會(huì)漏標(biāo)次屠。因此重新標(biāo)記階段仍然需要掃描新生代。

預(yù)處理階段都干了啥雳刺?

預(yù)處理階段其實(shí)有2部分:

  • 預(yù)清理階段
  • 可終止的預(yù)處理

這個(gè)階段的目的都是為了減輕后面的重新標(biāo)記的壓力劫灶,提前做一點(diǎn)重新標(biāo)記階段的工作。一般CMS的GC耗時(shí)80%都在remark階段掖桦,所以預(yù)處理階段也是為了減少remark階段的STW時(shí)間本昏。

重新標(biāo)記階段需要做一下工作:

  1. 遍歷新生代對(duì)象,重新標(biāo)記
  2. 根據(jù)GC Roots枪汪,重新標(biāo)記
  3. 遍歷老年代的Dirty Card涌穆,重新標(biāo)記(這里的Dirty Card大部分已經(jīng)在clean階段處理過)

遍歷新生代對(duì)象時(shí),可能很多對(duì)象已經(jīng)是不可達(dá)了料饥,但是還是需要掃描蒲犬。遍歷Dirty Card做處理。

這2部分其實(shí)就是預(yù)處理階段幫助重新標(biāo)記減輕壓力的地方

  • 預(yù)清理階段和可終止的預(yù)處理都會(huì)掃描Dirty Card做處理
  • 可終止的預(yù)處理岸啡,盡量進(jìn)行一次ygc原叮,讓不可達(dá)的對(duì)象被回收掉,remark階段遍歷新生代的對(duì)象成本小一點(diǎn)

具體這個(gè)階段的詳情見參考文檔圖解CMS垃圾回收機(jī)制,你值得擁有

參考文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市唯欣,隨后出現(xiàn)的幾起案子嘹吨,更是在濱河造成了極大的恐慌,老刑警劉巖境氢,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蟀拷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡萍聊,警方通過查閱死者的電腦和手機(jī)问芬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來寿桨,“玉大人此衅,你說我怎么就攤上這事⊥っ” “怎么了挡鞍?”我有些...
    開封第一講書人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)预烙。 經(jīng)常有香客問我墨微,道長(zhǎng),這世上最難降的妖魔是什么默伍? 我笑而不...
    開封第一講書人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任欢嘿,我火速辦了婚禮,結(jié)果婚禮上也糊,老公的妹妹穿的比我還像新娘炼蹦。我一直安慰自己,他們只是感情好狸剃,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開白布掐隐。 她就那樣靜靜地躺著,像睡著了一般钞馁。 火紅的嫁衣襯著肌膚如雪虑省。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評(píng)論 1 284
  • 那天僧凰,我揣著相機(jī)與錄音探颈,去河邊找鬼。 笑死训措,一個(gè)胖子當(dāng)著我的面吹牛伪节,可吹牛的內(nèi)容都是我干的光羞。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼怀大,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼纱兑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起化借,我...
    開封第一講書人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤潜慎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后蓖康,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铐炫,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年蒜焊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了驳遵。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡山涡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出唆迁,到底是詐尸還是另有隱情鸭丛,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布唐责,位于F島的核電站鳞溉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏鼠哥。R本人自食惡果不足惜熟菲,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望朴恳。 院中可真熱鬧抄罕,春花似錦、人聲如沸于颖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽森渐。三九已至做入,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間同衣,已是汗流浹背竟块。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留耐齐,地道東北人浪秘。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓蒋情,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親秫逝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子恕出,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344