面試官:今天還是來聊聊CMS垃圾收集器唄?
候選者:嗯啊...
候選者:如果用Seria和Parallel系列的垃圾收集器:在垃圾回收的時,用戶執行緒都會完全停止,直至垃圾回收結束!
候選者:CMS的全稱:Concurrent Mark Sweep,翻譯過來是「并發標記清除」
候選者:用CMS對比上面的垃圾收集器(Seria和Parallel和parNew):它最大的不同點就是「并發」:在GC執行緒作業的時候,用戶執行緒「不會完全停止」,用戶執行緒在「部分場景下」與GC執行緒一起并發執行,
候選者:但是,要理解的是,無論是什么垃圾收集器,Stop The World是一定無法避免的!
候選者:CMS只是在「部分」的GC場景下可以讓GC執行緒與用戶執行緒并發執行
候選者:CMS的設計目標是為了避免「老年代 GC」出現「長時間」的卡頓(Stop The World)

面試官:那你清楚CMS的作業流程嗎?
候選者:只了解一點點,不能多了,
候選者:CMS可以簡單分為5個步驟:初始標記、并發標記、并發預清理、重新標記以及并發清除
候選者:從步驟就不難看出,CMS主要是實作了「標記清除」垃圾回收演算法
面試官:嗯...是的
候選者:我就從「初始標記」來開始吧
候選者:「初始標記」會標記GCRoots「直接關聯」的物件以及「年輕代」指向「老年代」的物件
候選者:「初始標記」這個程序是會發生Stop The World的,但這個階段的速度算是很快的,因為沒有「向下追溯」(只標記一層)

候選者:在「初始標記」完了之后,就進入了「并發標記」階段啦
候選者:「并發標記」這個程序是不會停止用戶執行緒的(不會發生 Stop The World),這一階段主要是從GC Roots向下「追溯」,標記所有可達的物件,
候選者:「并發標記」在GC的角度而言,是比較耗費時間的(需要追溯)

候選者:「并發標記」這個階段完成之后,就到了「并發預處理」階段啦
候選者:「并發預處理」這個階段主要想干的事情:希望能減少下一個階段「重新標記」所消耗的時間
候選者:因為下一個階段「重新標記」是需要Stop The World的
面試官:嗯...
候選者:「并發標記」這個階段由于用戶執行緒是沒有被掛起的,所以物件是有可能發生變化的
候選者: 可能有些物件,從新生代晉升到了老年代,可能有些物件,直接分配到了老年代(大物件),可能老年代或者新生代的物件參考發生了變化...
面試官:那這個問題,怎么解決呢?
候選者:針對老年代的物件,其實還是可以借助類card table的存盤(將老年代物件發生變化所對應的卡頁標記為dirty)
候選者:所以「并發預處理」這個階段會掃描可能由于「并發標記」時導致老年代發生變化的物件,會再掃描一遍標記為dirty的卡頁
面試官:嗯...
候選者:對于新生代的物件,我們還是得遍歷新生代來看看在「并發標記」程序中有沒有物件參考了老年代..
候選者:不過JVM里給我們提供了很多「引數」,有可能在這個程序中會觸發一次 minor GC(觸發了minor GC 是意味著就可以更少地遍歷新生代的物件)

候選者:「并發預處理」這個階段階段結束后,就到了「重新標記」階段
候選者:「重新標記」階段會Stop The World,這個程序的停頓時間其實很大程度上取決于上面「并發預處理」階段(可以發現,這是一個追趕的程序:一邊在標記存活物件,一邊用戶執行緒在執行產生垃圾)

候選者:最后就是「并發清除」階段,不會Stop The World
候選者:一邊用戶執行緒在執行,一邊GC執行緒在回收不可達的物件
候選者:這個程序,還是有可能用戶執行緒在不斷產生垃圾,但只能留到下一次GC 進行處理了,產生的這些垃圾被叫做“浮動垃圾”
候選者:完了以后會重置 CMS 演算法相關的內部資料,為下一次 GC 回圈做準備

面試官:嗯,CMS的回收程序,我了解了
面試官:聽下來,其實就是把垃圾回收的程序給"細分"了,然后在某些階段可以不停止用戶執行緒,一邊回收垃圾,一邊處理請求,來減少每次垃圾回收時 Stop The World的時間
面試官:當然啦,中間也做了很多的優化(dirty card標記、可能中途觸發minor gc等等,在我理解下,這些應該都提供了CMS的相關引數配置)
面試官:不過,我看現在很多企業都在用G1了,那你覺得CMS有什么缺點呢?
候選者:1.空間需要預留:CMS垃圾收集器可以一邊回收垃圾,一邊處理用戶執行緒,那需要在這個程序中保證有充足的記憶體空間供用戶使用,
候選者:如果CMS運行程序中預留的空間不夠用了,會報錯(Concurrent Mode Failure),這時會啟動 Serial Old垃圾收集器進行老年代的垃圾回收,會導致停頓的時間很長,
候選者:顯然啦,空間預留多少,肯定是有引數配置的
候選者:2. 記憶體碎片問題:CMS本質上是實作了「標記清除演算法」的收集器(從程序就可以看得出),這會意味著會產生記憶體碎片
候選者:由于碎片太多,又可能會導致記憶體空間不足所觸發full GC,CMS一般會在觸發full GC這個程序對碎片進行整理
候選者:整理涉及到「移動」/「標記」,那這個程序肯定會Stop The World的,如果記憶體足夠大(意味著可能裝載的物件足夠多),那這個程序卡頓也是需要一定的時間的,
面試官:嗯...

候選者:使用CMS的弊端好像就是一個死回圈:
候選者:1. 記憶體碎片過多,導致空間利用率減低,
候選者:2. 空間本身就需要預留給用戶執行緒使用,現在碎片記憶體又加劇了空間的問題,導致有可能垃圾收集器降級為Serial Old,卡頓時間更長,
候選者:3. 要處理記憶體碎片的問題(整理),同樣會卡頓
候選者:不過,技術實作就是一種trade-off(權衡),不可能你把所有的事情都做得很完美
候選者:了解這個程序,是非常有趣的
面試官:那G1垃圾收集器你了解嗎
候選者:只了解一點點,不能多了
候選者:不過,留到下次吧,先讓你消化下,不然怕你頂不住了,
本文總結:
-
CMS垃圾回收器設計目的:為了避免「老年代 GC」出現「長時間」的卡頓(Stop The World)
-
CMS垃圾回收器回收程序:初始標記、并發標記、并發預處理、重新標記和并發清除,初始標記以及重新標記這兩個階段會Stop The World
-
CMS垃圾回收器的弊端:會產生記憶體碎片&&需要空間預留:停頓時間是不可預知的


歡迎關注我的微信公眾號【Java3y】來聊聊Java面試,對線面試官系列持續更新中!
【對線面試官-移動端】系列 一周兩篇持續更新中!
【對線面試官-電腦端】系列 一周兩篇持續更新中!
原創不易!!求三連!!
更多的文章可往:文章的目錄導航轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/353571.html
標籤:Java
