CMS垃圾收集器

CMS垃圾收集器收集詳細步驟算法

  • 初始標記(Stop the world)
  • 併發標記
  • 預清理
  • 可被終止的預清理
  • 從新標記(Stop the world)
  • 併發清除
  • 併發重置

初始標記

標記GcRoots直接可達老年對象,新生代存活對象引用的老年代對象.整個過程在JDK1.7中是單線程的在JDK1.8中是多線程的(經過CMSParallelInitialMarkEnabled參數調整)。這個過程會致使STW。

初始標記標記的對象

併發標記

初始標記階段標記過的對象開始,標記其它存活對象,這個階段垃圾回收線程和應用線程同時運行。因爲是同時運行,應用線程還在跑,會致使對象的晉升,對象引用的變化,特殊對象直接分配到老年代。這些受到影響的老年代對象所在的Card會被標記成Dirty,用於從新標記階段掃描,老年代對象的Card被標記爲Dirty的可能緣由以下面綠線所示。

併發標記過程當中受到影響的對象

預清理

因爲上一個階段是併發執行的未標記的變化對象只是標記成了Dirty對象,尚未處理, 預清理就是來標記這些 Dirty對象。以下圖:在併發標記階段3號Card被標誌爲Dirty。這個階段是爲 從新標記階段作準備。

image-20201108102807805

預清理將6號標誌爲存活對象多線程

image-20201108103026903

可被終止的預清理

這個階段也是爲從新標誌階段作準備,在進入從新標誌階段前,最好能進行一個Minor GC,將年輕代清理一遍, 這樣能夠清除大部分年輕代的對象(絕大部分年輕代對象朝生夕死),儘可能縮短 從新標記階段停頓時間,CMS還提供了CMSScavengeBeforeRemark參數,能夠在進入從新標記以前強制進行依次Minor gc。

從新標誌(remark)

預清理可被終止的預清理都是爲 從新標誌階段作準備,因爲 從新標誌階段會發生(STW),因此要保證盡肯能的停頓時間段,否則就會影響應用程序的用戶體驗。這個階段掃描的目標是:年輕代+GC Roots+Dirty老年代對象,這個階段是多線程的(XX:+CMSParallelRemarkEnabled)。

併發清除

用戶線程被激活,那些未被標誌的對象會被清除。'

併發重置

CMS垃圾收集器參數回到初始狀態,爲下一次垃圾收集作準備。

使用CMS垃圾收集器要注意的問題

從新標記停頓時間過長

80%的時間花在 從新標誌階段,若是發現 從新標誌階段停頓時間過長,可嘗試添加 -XX:+CMSScavengeBeforeRemark,在 從新標誌以前作一次 Minor GC,目的是減小對老年代對象的無效引用,下降 從新標誌的開銷。

內存碎片問題

CMS是基於標記-清除算法的,CMS只會刪除垃圾對象,不會對內存空間作壓縮,會形成內存碎片。咱們須要用 -XX:CMSFullGCsBeforeCompaction=n參數來調整,含義是在上一次CMS併發執行事後,還要執行多少次 Full GC才作內存壓縮.

concurrent mode failure

在CMS GC過程當中因爲應用程序也在跑,當年輕代滿了,執行了 Minor GC這時候,須要將存活對象放入老年代,而此時老年代空間也不足,這時CMS尚未機會回收老年代。能夠設置如下兩個參數
  • -XX:CMSInitiatingOccupancyFraction=75
CMS對內存的佔用率達到75%將啓動GC,默認爲92%,過高將致使 promotion failed
  • -XX:+UseCMSInitiatingOccupancyOnly
若是沒有設置 UseCMSInitiatingOccupancyOnly,只設置了 CMSInitiatingOccupancyFraction那麼JVM只在第一次使用,後續會進行自動調整。

爲何要設置以上兩個參數,在垃圾收集階段,用戶線程還在運行,因此必需要留夠空間讓用戶線程運行。CMS前五個階段都是標記存活對象的,除了」初始標記」和」從新標記」階段會stop the word ,其它三個階段都是與用戶線程一塊兒跑的,就會出現這樣的狀況gc線程正在標記存活對象,用戶線程同時向老年代提高新的對象,清理工做尚未開始,old gen已經沒有空間容納更多對象了,這時候就會致使concurrent mode failure, 而後就會使用串行收集器回收老年代的垃圾,致使停頓的時間很是長。
CMSInitiatingOccupancyFraction參數要設置一個合理的值,設置大了,會增長concurrent mode failure發生的頻率,設置的小了,又會增長CMS頻率,因此要根據應用的運行狀況來選取一個合理的值。若是發現這兩個參數設置大了會致使full gc,設置小了會致使頻繁的CMS GC,說明你的老年代空間太小,應該增長老年代空間的大小了。併發

promotion failed

在進行Minor GC時,Survivor space放不下,對象只能放入老年代,而此時老年代也放不下,大多數狀況是老年代內存碎片太多,致使沒有連續的空間存放對象。

過早的晉升和晉升失敗

發生Minor GC時,若是對象過大(Survivor Space存放不下)基本上會放到老年代,這種現象被稱爲對象過早晉升,這將致使老年代被中短時間對象增張,肯能致使嚴重的性能問題。若是老年代也滿了,會觸發Full GC,這將會致使遍歷整個堆,晉升失敗。

解決方案

  • 若是是由於內存碎片致使的大對象提高失敗,cms須要進行空間整理壓縮;
  • 若是是由於提高過快致使的,說明Survivor 空閒空間不足,那麼能夠嘗試調大 Survivor;
  • 若是是由於老年代空間不夠致使的,嘗試將CMS觸發的閾值調低。

CMS總結

  • CMS只收集老年代,響應速度優先。
  • 從新標記會STW,停頓時間較長,因此在這以前進行一次Minor GC,會減小不少對老年代對象的無效引用。
  • 內存碎片問題致使的Full GC,請使用-XX:CMSFullGCsBeforeCompaction=n,有規律對內存進行整理減小內存碎片。
  • JDK1.7,JDK1.8設置CMS垃圾收集器XX:+UseConcMarkSweepGC

幾個重要的CMS參數

  • -XX:CMSFullGCsBeforeCompaction=n Full GC n 次後進行內存壓縮整理
  • -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly 內存佔用70%將觸發CMS GC
  • -XX:+CMSScavengeBeforeRemark CMS GC前執行一次Minor GC

相關文章
相關標籤/搜索