JVM CMS收集器

CMS收集器

  • CMS收集器,以獲取最短回收停頓時間爲目標,多數應用於互聯網站或者B/S系統的服務器端上。
  • CMS是基於“標記-清除” 算法實現的,整個過程分爲4個步驟

      1)初始標記 (CMS initial mark)

      2)併發標記(CMS concurrent mark)

      3)重新標記(CMS remark)

      4)併發清除(CMS concurrent sweep)

  • 其中,初始標記 和重新標記 這兩個步驟仍然需要stop the world
  • 初始標記只是標記一下 GC Roots 能直接關聯到的對象,速度很快;
  • 併發標記階段就是進行GC Roots Tracing的過程
  • 重新標記階段則是爲了修正併發標記期間因用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短。
  • CMS收集器的運作步驟如下,整個過程中耗時最長的併發標記和併發清除過程收集器線程都可以與用戶線程一起工作,因此總體上看,CMS收集器的內存回收過程是與用戶線程一起併發執行的。

   

優點:

  • 併發收集、低停頓

缺點:

  • cms收集器對cpu資源非常敏感
  • cms收集器無法處理浮動垃圾,可能出現“ concurrent mode failure” 失敗而導致另一次full gc 的產生。由於cms併發清理階段用戶線程還在運行着,伴隨程序運行自然就還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之後,cms無法在當次收集中處理它們,只好待下一次gc在清理掉,這一部分就稱爲浮動垃圾。也是由於在垃圾收集階段用戶線程還需要運行,那也就還需要預留有足夠的內存空間給用戶線程使用,因此cms收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了在進行收集,需要預留一部分空間提供併發收集時的程序運作使用。如果在應用中 老年代增長不是太快,可以適當調高參數-XX:CMSInitiatingOccupancyFraction的值來提供觸發百分比,以便降低內存回收次數從而獲取更好的性能。要是cms運行期間預留的內存無法滿足程序需要時,虛擬機將啓動後備預案;臨時啓用Serial old收集器來重新進行老年代的垃圾收集,這樣停頓時間就很長了。所以說參數-XX:CMSInitiatingOccupancyFraction 是設置太高很容易導致大量的“ concurrent mode failure” 失敗,性能反而降低
  • 收集結束時會有大量空間碎片產生,空間碎片過多時,將會給大對象分配帶來很大麻煩,往往出現老年代還有很大空間剩餘,但是無法找到足夠大的連續空間來分配當前對象,不得不提前進行一次Full GC。CMS收集器提供了一個-XX:UseCMSCompactAtFullCollection開關參數(默認是開啓的),用於在CMS收集器頂不住要進行full gc時 開啓內存碎片的合併整理過程,內存整理的過程是無法併發的,空間碎片問題沒有了,但停頓時間不得不變長。

空間分配擔保

  • 在發生Minor GC之前,虛擬機會先檢查老年代最大可用的連續空間是否大於新生代所有對象總空間,如果這個條件成立,那麼Minor GC可以確保是安全的。當大量對象在minor gc 後仍然存活,就需要老年代進行空間分配擔保,把survivor無法容納的獨享直接進入老年代,如果老年代判斷到剩餘空間不足(根據以往每一次回收晉升到老年代對象容聯的平均值作爲經驗值),則進行一次FULL GC。

CMS收集器收集步驟

      1)初始標記 ( initial mark)

      2)併發標記( concurrent mark)

      3)併發預清理( concurrent preclean)

      4)併發可失敗的預發清理( concurrent abortable preclean)

      5)最終標記 (final remark)

      6)併發清除(concurrent sweep)

      7)併發重置(concurrent reset)

  • initial mark 這個是 cms兩次 stop-the-world 事件的其中一次,這個階段的目標是:標記那些直接被gc goot引用或者被年輕代所引用的所有對象

         

  • concurrent mark 在這個階段 garbage collector 會遍歷老年代,然後標記所有存活的對象,它會根據上個階段找到的gc roots遍歷查找。併發標記階段,它會與用戶的應用程序併發運行。並不是老年代所有的存活對象都會被標記,因爲在標記期間用戶的程序可能會改變一些引用          
與階段1的圖對比,就會發現有一個對象的引用已經發生變化

 

  • concurrent preclean 這個也是個併發階段,與應用的線程併發運行,並不會stop應用的線程。在併發運行的過程中,一些對象的引用可能會發生變化,但是這種情況發生時,jvm會將包含這個對象的區域(card)標記爲 dirty,這也就是card marking。在pre-clean階段,那些能夠從dirty對象到達的對象也會被標記,這個標記做完之後,dirty card標記就會被清除了

             

  • concurrent abortable preclean 也是個併發階段,但是同樣不會影響用戶的應用線程,這個階段是爲了儘量承擔STW中最終標記階段的工作。這個階段持續時間依賴於很多的因素,由於這個階段是在重複做很多相同的工作,直接滿足一些條件(比如:重複迭代的次數、完成的工作量或者時鐘時間等) 
  • final remark 這是第二個stw 階段,也是cms中最後一個,這個階段的目標是標記老年代所有的存活對象,由於之前的階段是併發執行的,gc線程可能跟不上應用程序的變化,爲了完成標記老年代所有存活對象的目標,STW就非常有必要了。通常CMS的final remark階段會在年輕代儘可能乾淨的時候運行,目的是爲了減少連續的stw發生的可能性(年輕代存活對象過多的話,也會導致老年代涉及的存活對象很多)。這個階段會比前面幾個階段更復雜一些。
  • 標記階段完成 ;經歷過這五個階段之後,老年代所有存活的對象都被標記過了,現在可以通過清除算法去清理那些老年代不再使用的對象。
  • concurrent sweep 這裏不需要stw,它是與用戶的應用程序併發運行,這個階段是:清除那些不再使用的對象,回收它們的佔用空間爲將來使用

           

  • concurrent reset 這個階段也是併發執行,它會重設cms內部的數據結構,爲下次gc 做準備。

總結

  • cms通過將大量的工作分散到併發處理階段來減少stw時間,在這塊做的非常優秀,但是cms也有一些其他問題
配置jvm 參數如下:
-verbose:gc
-Xms20M
-Xmx20M
-Xmn10M
-XX:+PrintGCDetails
-XX:SurvivorRatio=8
-XX:+UseConcMarkSweepGC
package com.jvm.gc;

public class MyTest5 {

    public static void main(String[] args) {
        int size = 1024 * 1024;

        byte[] myAlloc1 = new byte[4 * size];

        System.out.println("MyTest5.main 1111111");

        byte[] myAlloc2 = new byte[4 * size];

        System.out.println("MyTest5.main 1222222");
        byte[] myAlloc3 = new byte[4 * size];

        System.out.println("MyTest5.main 33333333");
        byte[] myAlloc4 = new byte[2 * size];

        System.out.println("MyTest5.main 444444444");

    }

}


打印結果:

 當輸出yTest5.main 1111111 時沒有進行垃圾回收,因爲初始新生代有9M的空間,myAlloc1對象4M 生成在eden區

 當輸出 MyTest5.main 1222222的時候,需要生成myAlloc2對象4M,此時eden區只有8M空間,因此進行垃圾回收[GC (Allocation Failure) [ParNew: 5596K->403K(9216K), 0.0037494 secs] 5596K->4501K(19456K), 0.0037929 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 

 當輸出MyTest5.main 33333333 ,新生代、老年代進行垃圾回收

[GC (CMS Initial Mark) [1 CMS-initial-mark: 8196K(10240K)] 12846K(19456K)  8196K表示老年代存活對象佔用堆空間大小;10240K表示老年代總空間大小;此時表明 有兩個新生代4M對象晉升到老年代,因爲新生代大小總共8M,放不下。

MyTest5.main 1111111
[GC (Allocation Failure) [ParNew: 5596K->403K(9216K), 0.0037494 secs] 5596K->4501K(19456K), 0.0037929 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
MyTest5.main 1222222
[GC (Allocation Failure) [ParNew: 4740K->554K(9216K), 0.0041357 secs] 8838K->8750K(19456K), 0.0041578 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
[GC (CMS Initial Mark) [1 CMS-initial-mark: 8196K(10240K)] 12846K(19456K), 0.0002583 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
[CMS-concurrent-mark-start]
MyTest5.main 33333333
[CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[CMS-concurrent-abortable-preclean-start]
[CMS-concurrent-abortable-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (CMS Final Remark) [YG occupancy: 7074 K (9216 K)][Rescan (parallel) , 0.0002725 secs][weak refs processing, 0.0000060 secs][class unloading, 0.0001792 secs][scrub symbol table, 0.0003598 secs][scrub string table, 0.0001870 secs][1 CMS-remark: 8196K(10240K)] 15270K(19456K), 0.0010517 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
[CMS-concurrent-sweep-start]
MyTest5.main 444444444[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 

[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
Heap
 par new generation   total 9216K, used 7320K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
  eden space 8192K,  82% used [0x00000007bec00000, 0x00000007bf29bb70, 0x00000007bf400000)
  from space 1024K,  54% used [0x00000007bf400000, 0x00000007bf48a800, 0x00000007bf500000)
  to   space 1024K,   0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
 concurrent mark-sweep generation total 10240K, used 8196K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
 Metaspace       used 2977K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 328K, capacity 388K, committed 512K, reserved 1048576K

Process finished with exit code 0

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章