JVM - CMS深度剖析
文章目錄
- Pre
- 概述
- 階段
- 初始標記 (STW)
- 并發標記 (用戶線程和GC線程并行工作)
- 重新標記 (STW)
- 并發清理(用戶線程和GC線程并行工作)
- 并發重置
- concurrent mode failure 是怎么回事兒
- CMS核心參數
- CMS的優缺點
- ParNew + CMS設置Demo
- GC算法底層實現
- 三色標記
Pre
JVM-04垃圾收集Garbage Collection(上)【垃圾對象的判定】
JVM-05垃圾收集Garbage Collection(中)【垃圾收集算法】
JVM-06垃圾收集Garbage Collection(下)【垃圾收集器】
JVM - 再聊GC垃圾收集算法及垃圾收集器
概述
Concurrent Mark Sweep 并發標記清除 。
CMS 收集器是一種以獲取最短回收停頓時間為目標的收集器。
它非常符合在注重用戶體驗的應用上使用,它是HotSpot虛擬機第一款真正意義上的并發收集器,它第一次實現了讓垃圾收集線程與用戶線程(基本上)同時工作 。
從名字( Mark Sweep )上也可以看出 CMS收集器是一種 “標記-清除”算法實現的垃圾收集器。
階段
初始標記 (STW)
初始標記階段,會Stop the word , 這個階段CMS僅標記被GC Root直接引用的對象,所以速度很快。
為什么這個階段要STW呢?
試想一下,如果初始標記階段 GC線程和用戶線程同時運行,GC的過程中,用戶線程還是會有新的垃圾對象產生 ,那啥時候能標記完,徒增復雜。 同時CMS設計的理念就是用戶感知至上,所以雖然STW,但也盡量縮短STW的時間,所以選擇了僅標記被GC Root直接引用的對象,而無需遍歷整個堆 。
并發標記 (用戶線程和GC線程并行工作)
經過了上一步的初始標記, 已經將GC Root 直接引用的對象標記完成。
CMS 老年代的Garbage Collector , 回收的是整個堆的垃圾對象,效率隨著堆的大小成反比 ( 如果堆比較大,比如10G,CMS也是有點吃力的,所以才有了G1)
為了避免GC時間過長,這個階段CMS 讓用戶線程和 GC線程同時工作,盡量減少用戶線程的停頓。 GC線程這個時候就要從GC Roots的直接關聯對象開始遍歷整個對象圖,這個耗時最長。 所以CMS重點優化了這塊 。 I
用戶線程和GC線程并行工作,多少都會存在一些問題 。因為用戶程序繼續運行,可能會有導致已經標記過的對象狀態發生改變。
試想一下 GC工作的時候,用戶線程也在工作
怎么辦呢? CMS設計了 下個階段 重新標記 來修復這個階段因對象狀態變更導致的標記錯誤。
重新標記 (STW)
這個階段主要是修正并發標記期間因為用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄。 所以這個時候要STW,不然還是會出現階段二的情況。
這個階段的停頓時間一般會比初始標記階段的時間稍長,遠遠比并發標記階段時間短
如何重新標記呢? 這里只要采用 三色標記中的增量更新算法。
并發清理(用戶線程和GC線程并行工作)
又是并行執行
如果這個清理階段又有新的對象進來(肯定沒有被標記,因為上一步已經標記過了),這個時候怎么辦? 也刪掉? 還不是垃圾對象啊 刪掉那肯定不行。該怎么辦呢?
CMS是這樣處理的: 對于新增對象會被標記為黑色不做任何處理
這樣的對象被稱為 【浮動垃圾】, 標記黑色,本次不處理,等下次GC。
并發重置
經過這么一輪的標記,最后的階段 肯定是把 標記清除掉,等待下一輪 ~
concurrent mode failure 是怎么回事兒
本身full GC 就是因為老年代沒有空間了, 在 并發標記 和 并發清除階段 是 用戶線程和GC線程 并行執行, 如果這個時候 用戶線程又產生了一些大對象或者符合條件的對象晉升到了老年代, 這個時候 老年代沒有空間存放這些對象了,GC一邊回收,系統一邊運行,也許沒回收完就再次觸發full gc , 就出現了 “concurrent mode failure” .
出現這個情況,CMS是怎么處理呢? 直接OOM?
顯然不是,CMS會Stop the word , 然后使用Serial Old 單線程 來進行垃圾回收。 盡量避免這種情況,效率非常低。
如何避免呢? 可以合理設置CMS的參數 (-XX:CMSInitiatingOccupancyFraction) 默認92% ,可以將這個值設置為80%(根據業務考量該值,你的系統大對象多的話 ,當然了,你設置了 80% 就意味著你老年代將會有20%的空間不可用,這部分空間僅能用來存放GC過程中新的對象,需要合理評估),盡量避免并發失敗的情況的發生。
CMS核心參數
- -XX:+UseConcMarkSweepGC:啟用cms
- -XX:ConcGCThreads:并發的GC線程數
- -XX:+UseCMSCompactAtFullCollection:FullGC之后做壓縮整理,其目的是為了減少內存碎片
- -XX:CMSFullGCsBeforeCompaction:多少次FullGC之后壓縮一次,默認是0,代表每次FullGC后都會壓縮一次
- -XX:CMSInitiatingOccupancyFraction: 當老年代使用達到該比例時會觸發FullGC(默認是92%)
- -XX:+UseCMSInitiatingOccupancyOnly:只使用設定的回收閾值(-XX:CMSInitiatingOccupancyFraction設定的值),如果不指定,JVM僅在第一次使用設定值,后續則會自動調整
- -XX:+CMSScavengeBeforeRemark:在CMS GC前啟動一次minor gc,目的在于減少老年代對年輕代的引用,降低CMS GC的標記階段時的開銷,一般CMS的GC耗時 80%都在標記階段
- -XX:+CMSParallellnitialMarkEnabled:表示在初始標記的時候多線程執行,縮短STW的時間
- -XX:+CMSParallelRemarkEnabled:在重新標記的時候多線程執行,縮短STW的時間
CMS的優缺點
優點
缺點:
ParNew + CMS設置Demo
-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=5 -XX:PretenureSizeThreshold=1M -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=3沒有標準答案,需要依據自己的業務特點,進行合理的參數調優。
GC算法底層實現
三色標記
在并發標記的過程中,應用線程也在運行,所以GC線程在運行期間應用線程使用的對象之間的引用可能發生變化,會存在多標和漏標的現象。
JVM把Gcroots可達性分析遍歷對象過程中遇到的對象, 按照“是否訪問過”這個條件標記成以下三種顏色 : 黑 灰 白
-
黑色: 表示對象已經被垃圾收集器訪問過, 且這個對象的所有引用都已經掃描過。 黑色的對象代表已經掃描過, 它是安全存活的, 如果有其他對象引用指向了黑色對象, 無須重新掃描一遍。 黑色對象不可能直接(不經過灰色對象) 指向某個白色對象。
-
灰色: 表示對象已經被垃圾收集器訪問過, 但這個對象上至少存在一個引用還沒有被掃描過。
-
白色: 表示對象尚未被垃圾收集器訪問過。 顯然在可達性分析剛剛開始的階段, 所有的對象都是白色的, 若在分析結束的階段, 仍然是白色的對象, 即代表不可達。
總結
以上是生活随笔為你收集整理的JVM - CMS深度剖析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JVM - 再聊GC垃圾收集算法及垃圾收
- 下一篇: JVM - G1初探