咱們上線Java服務的時候須要對其配置一些JVM參數,如堆空間大小、虛擬機棧大小、垃圾回收算法。對於年輕代和老年代咱們能夠配置不一樣的垃圾回收算法。在一些對rt
要求很高的場景,服務不能有長時間的卡頓,CMS
就能夠運用於此場景。算法
Concurrent Mark Sweep
,是一款基於併發、使用標記清除算法的垃圾回收算法,只針對老年代進行垃圾回收。CMS收集器工做時,GC工做線程和用戶線程能夠併發執行,以達到下降STW
時間的目的。segmentfault
開起VM選項-XX:+UseConcMarkSweepGC
,表示對老年代的回收採用CMS。安全
首先,咱們須要釐清一個概念,即只有標記
階段才須要STW
。標記完成之後,須要清除的對象已經肯定,不管此時是否產生新的垃圾,都不影響對這些對象的清理。也就是說,清除
階段是能夠設計成和用戶線程併發執行的。數據結構
JVM在暫停的時候,須要選準一個時機,因爲JVM系統運行期間的複雜性,不可能作到隨時暫停,所以引入了安全點(safepoint)
的概念:程序只有在運行到安全點的時候,才能夠暫停下來。HotSpot
採用主動中斷的方式,讓執行線程在運行期輪詢是否須要暫停的標誌,若須要則中斷掛起。HotSpot
使用了幾條短小精煉的彙編指令即可完成安全點輪詢以及觸發線程中斷,所以對系統性能的影響幾乎能夠忽略不計。併發
可達性
是指,若是一個對象會被至少一個程序中的可達對象經過直接或間接的方式引用,則稱該對象是可達的
。更詳細地說,一個對象知足一下兩個條件之一,即被斷定爲可達的。性能
1.自己是根對象。根(root)是指由堆之外空間訪問的對象。JVM會將如下對象標記爲根:a.虛擬機棧(棧幀中的本地變量表)中引用的對象;b.方法區中的類靜態屬性引用的對象;c.方法區中的常量引用的對象;d.本地方法棧中JNI的引用對象。spa
2.被一個可達的對象引用。線程
CMS
將可達性分析分解成兩個階段:a.僅掃描與根節點直接關聯的對象; b.繼續向下掃描完全部對象。所以,標記
階段也被拆分紅兩個階段,即初始標記
和併發標記
。設計
CMS完整的收集過程以下:日誌
初始標記(init-mark)
:僅掃描與根節點直接關聯的對象並標記,這個階段必須STW
, 因爲跟節點數量有限,因此這個過程很是短暫。併發標記(concurrent-marking)
:與用戶線程併發標記。這個階段在初始標記的基礎上繼續向下追溯標記。在併發標記階段,用戶線程和標記線程併發執行,因此用戶不會感覺到停頓。併發預清理(concurrent-precleaning)
:與用戶線程併發進行。在併發標記階段一些對象的引用已經發生了變化,precleaning
會發現這些引用關係的改變,並將存活的對象標記。舉個例子:若是線程A有一個指向對象X的引用,並將該引用傳遞給了線程B,CMS須要記錄下線程B持有了對象X,即便線程A已經不存在了。precleaning
是爲了減小下一階段「從新標記」的工做量,由於remark
階段會STW
。從新標記(remark)
: remark
階段會STW
。若是應用正在併發運行且在不斷地改變對象引用,CMS
則不能準確地肯定某個對象是否存活。因此CMS
會在remark
階段STW
,從而獲取全部引用關係的改變。併發清理(concurrent-sweeping)
:清理垃圾對象,這個階段GC線程和用戶線程併發執行。併發重置(concurrent-reset)
:重置CMS收集器的數據結構,作好下一次執行GC任務的準備工做。
線上某服務的老年代配置了CMS,但卻在gc.log發現連續Full GC的問題。JVM參數配置以下:
-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=68
參數的意義是:在老年代到68%
的時候,會觸發一次CMS GC,應該是出現相似以下的日誌:
T20:10:37.803+0800: 3246087.559: [CMS-concurrent-mark-start] T20:10:38.463+0800: 3246088.220: [CMS-concurrent-mark: 0.661/0.661 secs] [Times: user=3.17 sys=0.56, real=0.66 secs] T20:10:38.463+0800: 3246088.220: [CMS-concurrent-preclean-start] T20:10:38.552+0800: 3246088.309: [CMS-concurrent-preclean: 0.069/0.089 secs] [Times: user=0.14 sys=0.04, real=0.09 secs]_</span> T20:10:38.552+0800: 3246088.309: [CMS-concurrent-abortable-preclean-start]
但線上環境的日誌卻出現以下的狀況:
老年代配置了900M,但卻在只使用了50+M的時候觸發了Full GC,並且是在短暫的時間內連續觸發。
配置了CMS卻觸發Full GC,有如下幾種可能:
經筆者回憶,在中午快12點的時候確實登陸過線上機,執行過jmap -histo:live
命令,經驗證,手動執行jmap -histo:live
,也確實會在gc.log出現觸發 Full GC的現象,問題獲得驗證。