其實網上有很多這類的文章,但是主要最近閒來無事,就寫一下自己在經過了各類書籍的研究後得出的一些自己的見解。
那麼CMS的大部分流程其實都有說到,那麼我們就從每個細節開始講解一下吧。
1.初始標記
2.併發標記
3.重新標記
4.併發清除
一.首先是第一步:初始標記,該過程會導致STW,這個過程是爲了掃描JVM當中的根對象(GC roots)以及直接關聯的對象,所謂的根對象一般來說的話通常是指(1)棧中所使用到的對象。(2)本地方法所使用到的對象。(例如native)。(3)Class對象。(4)靜態+final變量。(5)老年代對象(並不是說所有老年代對象,是指card table中的老年代對象,也就是指那些有年輕代引用的老年代對象,這是種以空間換時間的方法,分代GC年輕代時,不用掃描老年代,節約時間)。等等(可以參考的文章)https://blog.csdn.net/xhh198781/article/details/42213847
那麼這個時候掃描的時候就有兩種方式,(1)廣度優先(2)深度優先。那麼CMS選擇的是深度優先,這樣的好處比較明顯,因爲計算機有一個預讀機制,那麼這個時候深度優先就會比廣度優先更有效率。那麼找到之後需要如何標記喃?
這個時候就會出現一種標記方法,三色標記法(黑白灰)。剛開始掃描一個對象的時候會先把它標記爲灰色,然後去遍歷其引用
(1)沒有引用了,那麼會把他變爲黑色。
(2)遍歷其引用,首先將他變爲黑色,然後將該引用變爲灰色,接着遍歷引用。
那麼JVM是如何處理顏色喃?
(1)黑:遍歷完畢,不是垃圾
(2)灰:正在遍歷
(3)白:垃圾
那麼初始標記只會有兩種顏色,黑色和灰色。那麼這個時候第一步就走完了。
二.併發標記。
就是併發標記第一步標記的對象,把目前存活的對象標記上黑色。
由於叫做併發標記,那麼何謂併發?
就是指GC線程和用戶線程一起運行。那麼中間就有可能出現對象關係發生改變的情況了。那麼我們舉個例子
下圖最開始的關係是 A -> B -> C(A擁有B的引用,B擁有C的引用)。
那麼在第二步剛好掃描到B的時候,用戶線程將關係變成了A ->C ,B變成無引用。
那麼這個時候C就可能在系統中找不到了,因爲A已經是黑色了,不會再根據A去搜索了。
所以這個時候CMS提供了一種write barrier。
write barrier是指記錄當時引用的變化情況。
當整個環境中不存在灰色的時候就是重新標記的時候了。
三.重新標記
所謂的重新標記是指將write barrier中的關係變化修改到併發標記的三色標記當中,例如上圖,這個時候首先會進行STW,然後將C標記爲灰色,繼續遍歷即可。完成之後會進行下一步併發清除。
四.併發清除
併發清除是GC線程和用戶線程一起執行的,那麼這個時候新生成的對象是不會在這次GC範圍之中的,那麼就會有浮動垃圾。
那麼只會處理在這次GC中被標記爲白色的了。那麼這個時候如果白色仍然變爲了存活對象會怎麼辦喃,這個時候還是依靠writer barrier來保證清除掉的一定是垃圾。
如果只是標記清除即可,如果發生整理,和將年輕代移動到老年代的時候,就需要涉及到修改指針的問題了,
那麼流程大概是將該對象進行復制,並將其移動,在原來的位置處,記錄一個forward地址,該forward地址是移動後的位置,然後找到引用該對象的老年代以及當前一級引用對象,修改其引用地址即可,如果後續有對象引用該對象,那麼直接將後續對象的引用地址修改即可。