併發的標記—清除(Concurrent Mark Sweep,縮寫爲 CMS)收集器,使得在整個收集的過程當中只是很短的暫停應用的執行,可經過在 JVM 參數中設置-XX:UseConcMarkSweepGC 來使用此收集器,不過此收集器僅用於old和Perm(永生)的對象收集,併發的標記—清除較之Stop-The-World 的標記—清除複雜了不少,來看看:數組
併發標記—清除作到的是在標記訪問每個節點時以及清除不活躍的對象時採用和應用併發的方式,僅需在初始化標記節點狀態以及最終標記節點狀態時須要暫停整個應用,所以其形成的應用的暫停的時間會比較的短。併發
併發標記—清除爲了保證儘可能短的形成應用的暫停,首先從分配內存上作了改動,CMS提供了兩個 free lists,一個用於存放小對象,另一個則用於存放大對象,當 JVM 須要給對象分配內存時,則經過 free list 來找到可用的堆地址,並進行內存的分配以及將此地址從 free
list 刪除,當 CMS 回收對象內存後,則將其相應的地址從新放入此 free list 中,這樣的好處是在回收對象的時候不須要作對象的移動等,所以可讓回收過程併發的進行。jvm
接着來看看併發標記—清除的執行步驟:性能
1.Initial Marking --初始標記
此步須要暫停整個應用,JVM 掃描整個 old generation 中根對象可直接訪問到的對象,並對這些對象進行標記,對於標記的對象 CMS 採用一個外部的 bit 數組來進行記錄。spa
2. Concurrent Marking --併發標記
在初始化標記完畢後,CMS 恢復全部應用的線程,同時開始併發的對以前標記過的對象進行輪循,以標記這些對象可訪問的對象。CMS 爲了確保可以掃描到全部的對象,避免在 Initial Marking 中還有未標識到的對象,採用的方法爲找到標記了的對象,並將這些對象放入 Stack 中,掃描時尋找此對象依賴的對象,若是依賴的對象的地址在其以前,則將此對象進行標記,並同時放入 Stack 中,如依賴的對象地址在其以後,則僅標記該對象。線程
在進行 Concurrent Marking 時 minor GC 也可能會同時進行,這個時候很容易形成舊生代對象引用關係改變,CMS 爲了應對這樣的並發現象,提供了一個 Mod Union Table 來進行記錄,在這個 Mod Union Table 中記錄每次 minor GC 後修改了的 Card 的信息。設計
在進行 Concurrent Marking 時還有可能會出現的一個並發現象是應用修改了舊生代中的對象的引用關係,CMS 中仍然採用 Card Table 的方式來進行記錄,在 Card 中將某對象標識爲 dirty 狀態,但即便是這樣仍然可能會出現一種現象致使再也不被引用的對象仍然是 marked的狀態:3d
3.Final Marking -- 最終標記
此步須要暫停整個應用,因爲在 Concurrent Marking 時應用可能會修改對象的引用關係或建立新的對象,所以須要把這些改變或新建立的對象也進行掃描,CMS 遞歸掃描 Mod Union Table 以及 Card Table 中 dirty 的對象,並進行標記。對象
4.Concurrent Sweeping -- 併發清除
在完成了 Final Marking 後,恢復全部應用的線程,就進入到這步了,這步須要負責的是將沒有標記的對象進行回收。blog
回收過程是併發進行的,而 JVM 分配對象內存(儘管 CMS 僅用於 old generation,但有些時候會因爲應用建立的對象過大致使直接分配到 old generation 的現象,另一種現象就是 young generation 通過回收後須要轉入 old generation 的對象)和 CMS 釋放內存又都是操
做 free list,會產生 free list 競爭的現象,所以 CMS 在此增長了 Mutual exclusion locks,以 JVM分配優先。
CMS 爲了不每次回收後回收到的大小都比以前分配出去的內存小,在進行 sweeping的時候,還會盡可能的將相鄰的塊從新組裝爲一個塊,sweeping 爲了不和 JVM 分配對象內存產生衝突,採用的方法爲首先從 free list 中刪除塊,組裝完畢後再從新放入塊中,爲了可以從 free list 中刪除指定的塊,CMS 將 free list 設計爲了雙向鏈表。
總結:
CMS 中的耗時的過程都是和應用併發進行的,這也是 CMS 最突出的優勢,使得其形成的應用的暫停時間比 Mark-Sweeping 的方式短了不少,但同時也意味着 CMS 會和應用線程爭搶 CPU 資源, CMS 回收內存的方式也使得其很容易產生內存碎片,下降了空間的利用率,
另外就是 CMS 在回收時容易產生一些應該回收但須要等到下次 CMS 才能被回收掉的對象,例如上圖中的 C 對象,稱爲「浮動垃圾「,這也就要求了採用 CMS 的狀況下須要提供更多的可用的舊生代空間,整體來講 CMS 很適用於對響應時間要求很高、CPU 資源競爭不是很激烈以及內存空間相對更充足的系統。
MS 爲了下降和應用爭搶 CPU 資源的現象發生,還提供了一種增量的模式,稱爲 i-CMS,在這種模式下,CMS 僅啓動一個處理器線程來併發的掃描標記和清除,而且該線程在執行一小段時間後就會先將 CPU 使用權讓出來,分屢次多段的方式來完成整個掃描標記和清除
的過程,這樣下降了對於 CPU 資源的消耗,但同時也下降了 CMS 的性能,所以僅適用於 CPU少的應用。
CMS 爲了減小產生的內存碎片,提升 jvm 空間的利用率,提供了一個整理碎片的功能,可經過在 jvm 中指定-XX:+ UseCMSCompactAtFullCollection (開啓對內存空間的整理工做)來啓動此功能,在啓動了此功能後默認爲每次 Full GC 的時候都會進行整理,也能夠經過-XX:CMSFullGCsBeforeCompaction=來指定多少次 Full GC 後才執行整理,不過要注意的是,整理這個步驟是須要暫停整個應用的。
[--> 注 <--]
Mod Union Table ----在併發標記階段Minor GC 形成的對象之間引用的變化
Card Table --- 在併發標記階段應用程序自己形成對象之間引用的變化。