JVM垃圾收集器與內存分配策略(三)—— 垃圾收集器

1、Serial收集器

Serial收集器時最基礎、歷史最悠久的收集器,JDK1.3.1之前是HotSpot虛擬機新生代收集器的唯一選擇。這個收集器是一個單線程工作收集器,這裏的“單線程”的意義並不僅僅是說明它只會使用一個處理器或者一條收集線程去完成垃圾收集工作,更重要的是強調在它進行垃圾收集時,必須暫停其他所有工作線程,直到它收集結束(Stop The World)。
Serial/Serial Old收集器運行過程示意圖:
Serial/Serial Old收集器運行示意圖

2、ParNew收集器

ParNew收集器實質上時Serial收集器的多線程並行版本,除了同時使用多條線程進行垃圾收集之外,其餘的行爲包括Serial收集器可用的所有控制參數(例如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、對象分配原則、回收策略都與Serial收集器完全一致。ParNew收集器的工作過程如圖:
ParNew/Serial Old收集器運行示意圖
ParNew收集器除了支持多線程並行收集之外,其他與Serial收集器相比並沒有太多的創新之處,但它卻是不少運行在服務端模式下的HotSpot虛擬機,尤其是JDK7之前的遺留系統中首選的新生代收集器,其中有一個與功能、性能無關的但其實很重要的原因:除了Serial收集器外,目前只有它能與CMS收集器配合工作。

3、Parallel Scavenge收集器

Parallel Scavenge收集器也是一款新生代收集器,它同樣是基於標記-複製算法實現的收集器,也是能夠並行收集的多線程收集器。
Parallel Scavenge收集器的特點是它的關注點與其他收集器不同,CMS等收集器的關注點是儘可能地算短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量(Throughput)。所謂吞吐量就是處理器用於運行用戶代碼時間與處理器總消耗時間的比值。
Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量,分別是控制最大垃圾收集停頓時間的 -XX:MaxGCPauseMillis參數以及直接設置吞吐量大小的-XX:GCTimeRatio參數。

4、Serial Old收集器

Serial Old時Serial收集器的老年代版本,它同樣是一個單線程收集器,使用標記-整理算法。這個收集器的主要意義也是提供客戶端模式下的HotSpot虛擬機使用。如果在服務端模式下,它也可能有兩者用途:一種是在JDK5以及之前的版本中與Parallel Scavenge收集器搭配使用,另外一種是作爲CMS收集器發生失敗時的後背預案,在併發收集發生Concurrent Mode Failure時使用。

5、Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,支持多線程併發收集,基於標記-整理算法實現。這個收集器直到JDK6時纔開始提供,在此之前,新生代的Parallel Scavenge收集器一直處於相當尷尬的狀態,原因是如果新生代選擇了Parallel Scavenge收集器,老年代除了Seral Old收集器外別無選擇,其他表現良好的老年代收集器,如CMS無法與它配合工作。直到Parallel Old收集器出現後,“吞吐量優先”的收集器終於有了名副其實的搭配組合,在注重吞吐量或者處理器資源稀缺的場合,都可以優先考慮Parallel Scavenge加Parallel Old收集器這個組合。Parallel Old收集器工作過程如圖:
Parallel Scavenge/Parallel Old收集器運行示意圖

6、CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。目前很大一部分的Java應用集中在互聯網網站或者基於瀏覽器的B/S系統的服務端上,這類應用通常都會較爲關注響應速度,希望系統停頓時間儘可能短,已給用戶帶來良好的交互體驗。CMS收集器是基於標記-清除算法實現的,運行過程分爲以下四步:

  1. 初始標記(CMS initial mark)
  2. 併發標記(CMS concurrent mark)
  3. 重新標記(CMS remark)
  4. 併發清除(CMS concurrent sweep)

其中初始標記和重新標記的兩個步驟任然需要“Stop The World”。初始標記僅僅只是標記一下GC Roots 能直接關聯到的對象,速度很快;併發標記階段就是以GC Roots 的直接關聯對象開始遍歷整個對象圖的過程,這個過程耗時比較長但是不需要停頓用戶線程 ,可以與垃圾收集線程一起併發運行;而重新標記階段則是爲了修正併發標記期間,因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長一些,但也遠比並發標記階段的時間短;最後是併發清楚階段,清理刪除掉標記階段判斷的已經死亡的對象,由於不需要移動存活對象,所以這個階段也是可以與用戶線程同時併發的。
CMS收集器運行示意圖:
Concurrent Mark Sweep 收集器運行示意圖
CMS是一款優秀的收集器,它的主要優點在名字上已經體現出來了:併發收集、低停頓,一些官方公開文檔裏面稱之爲“併發低停頓收集器”(Concurrent Low Pause Collector)。CMS收集器是HotSpot虛擬機追求低停頓的第一次成功嘗試,但是它還遠遠達不到完美的程度,至少有以下三個明顯的缺點:

  1. CMS收集器對處理器資源非常敏感。事實上,面向併發設計的程序都對處理器資源比較敏感。在併發階段,它雖然不會導致用戶線程停頓,但卻會因爲佔用了一部分線程(或者說處理器的計算能力)而導致應用程序變慢,降低總吞吐量。
  2. CMS收集器無法處理“浮動垃圾”(Floating Garbage),有可能出現“Concurrent Mode Failure”失敗進而導致另一次完全“Stop The World” 的Full GC的產生。在CMS併發標記和併發清理階段,用戶線程還是在繼續運行的,程序自然就會伴隨有新的垃圾產生,但這一部分垃圾對象是出現在標記過程結束以後,CMS無法在當次收集中處理掉它們,只好留待下一次垃圾收集時再清理掉。這一部分垃圾稱爲“浮動垃圾”。
  3. CMS是一款基於“標記-清除”算法實現的收集器,收集結束時會有大量的空間碎片產生。空間碎片過多時會對大對象分配帶來很大麻煩,往往會出現老年代還有很多剩餘空間,但是無法找到足夠大的連續空間來分配當前對象,不得不提前觸發一次Full GC的情況。

參考書籍——《深入理解Java虛擬機》 周志明;

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