從前文
JVM垃圾回收幾種常見演算法和常見收集器
我們知道,CMS是老年代垃圾收集器。CMS 收集器主要關注系統停頓時間。CMS 是 Concurrent Mark Sweep 的縮寫,意為併發標記清除,從名稱上可以得知,它使用的是標記-清除演算法,同時它又是一個使用多執行緒併發回收的垃圾收集器。它可以與
Serial
收集器和
parNew
收集器搭配使用。
CMS工作時,主要步驟有:
初始標記、併發標記、重新標記、併發清除和併發重置
。其中
初始標記和重新標記
是獨佔系統資源的,而
併發標記、併發清除和併發重置
是可以和使用者執行緒一起執行的。因此,從整體上來說,CMS收集不是獨佔式的,它可以在應用程式執行過程中進行垃圾回收。
根據
標記-清除
演算法,
初始標記、併發標記和重新標記
都是為了標記出需要回收的物件。併發清理則是在標記完成後,正式回收垃圾物件;併發重置是指在垃圾回收完成後,重新初始化CMS資料結構和資料,為下一次垃圾回收做好準備。
CMS收集器在其主要的工作階段雖然沒有暴力地徹底暫停應用程式執行緒,但是由於它和應用程式執行緒併發執行,相互搶佔CPU,所以在CMS執行期內對應用程式吞吐量造成一定影響。CMS 預設啟動的執行緒數是 (
ParallelGCThreads+3)/4
),ParallelGCThreads 是新生代並行收集器的執行緒數,也可以透過
-XX:ParallelCMSThreads
引數手工設定CMS的執行緒數量。當CPU資源比較緊張時,受到CMS收集器執行緒的影響,應用程式的效能在垃圾回收階段可能會非常糟糕。
由於CMS收集器不是獨佔式的回收器,在CMS回收過程中,應用程式仍然在不停地工作。在應用程式工作過程中,又會不斷地產生垃圾。這些新生成的垃圾在當前CMS回收過程中是無法清除的。同時,因為應用程式沒有中斷,所以在CMS回收過程中,還應該確保應用程式有足夠的記憶體可用。因此,CMS收集器不會等待堆記憶體飽和時才進行垃圾回收,而是當前堆記憶體使用率達到某一閾值時,便開始進行回收,以確保應用程式在CMS工作過程中依然有足夠的空間支援應用程式執行。
這個回收閾值可以使用
-XX:CMSInitiatingOccupancyFraction
來指定,預設是 68。即當老年代的空間使用率達到 68%時,會執行一次CMS回收。如果應用程式的記憶體使用率增長很快,在CMS的執行過程中,已經出現了記憶體不足的情況,此時CMS回收將會失敗,JVM將啟動老年代序列收集器進行垃圾回收。如果這樣,應用程式將完全中斷,直到垃圾收集完成,這時,應用程式的停頓時間可能很長。因此,根據應用程式的特點,可以對-XX:CMSInitiatingOccupancyFraction 進行調優。如果記憶體增長緩慢,則可以設定一個稍大的值,大的閾值可以有效降低CMS的觸發頻率,減少老年代回收的次數可以較為明顯地改善應用程式效能。反之,如果應用程式記憶體使用率增長很快,則應該降低這個閾值,以避免頻繁觸發老年代序列收集器。
標記-清除演算法
將會造成大量記憶體碎片,離散的可用空間無法分配較大的物件。在這種情況下,即使堆記憶體仍然有較大的剩餘空間,也可能會被迫進行一次垃圾回收,以換取一塊可用的連續記憶體,這種現象對系統性能是相當不利的。為了解決這個問題,CMS收集器還提供了幾個用於記憶體壓縮整理的演算法。
-XX:+UseCMSCompactAtFullCollection
使CMS在垃圾收集完成後,進行一次記憶體碎片整理。記憶體碎片的整理並不是併發進行的。
-XX:CMSFullGCsBeforeCompaction
用於設定進行多少次CMS回收後,進行一次記憶體整理。
-XX:+CMSScavengeBeforeRemark
最終標記之前強制進行一個Minor GC。
-XX:+UseConcMarkSweepGC
可以要求新生代使用parNew,老年代使用CMS。
-XX:+UseCMSInitiatingOccupancyOnly
指定CMSInitiatingOccupancyFraction的回收閾值,如果不指定,jvm僅在第一次使用設定值,後續則自動調整。
-XX:+CMSParallelRemarkEnabled
並行執行最終標記階段,加快最終標記的速度
-XX:+CMSClassUnloadingEnabled
讓CMS可以收集永久帶,預設不會收集
-XX:+ExplicitGCInvokesConcurrent
當呼叫System。gc()的時候,執行並行gc,只有在CMS或者G1下該引數才有效
CMS對CPU的資源非常敏感。由於採用標記-清除演算法會存在空間碎片的問題,導致大物件無法分配空間不得不提前觸發一次full gc的情況。而且無法處理浮動垃圾,可能出現Concurrent Model Failure失敗而導致另一次full gc的情況。
垃圾回收篇幅過長,下文繼續講解最後的內容G1收集器。
想了解更多精彩內容,快來關注java技術閱讀