Java垃圾收集器——Serial,Parallel,CMS,G1收集器概述


1.概述

Java應用啟動的時候,除了配置Xms以及Xmx參數(Xmx:InitialHeapSize, Xms:MaxHeapSize),還需要選擇合適的垃圾收集器。
截止Jdk1.8,共提供了7款垃圾收集器,每一款垃圾收集器都具有不同的特點。我們所需要做的就是,根據Java應用的特點已經部署環境,確定不同垃圾收集器的組合。這幾款垃圾收集器之間聯系如下圖所示:

由上圖可知,Serial,ParNew,Parallel Scavenge主要負責Young generation區域的垃圾回收,CMS,Serial Odl, Parallel Old主要負責Tenured generation區域的垃圾回收,G1在Young generation以及Tenured generation區域均可以使用(詳細原因在下文會進行闡述)。

2.垃圾收集器概述

jdk提供了多重垃圾收集器,下文會提供主流的垃圾收集器搭配組合,各種組合按照特點分為以下三類:

  • 串行收集器:Serial + Serial Old;
  • 並行收集器: Parallel Scavenge + Parallel Old,專注於應用吞吐量;
  • 並發收集器:CMS,G1,專注於響應時間。

2.1 Serial收集器

Serial收集器(Serial + Serial Old)的主要特點是單線程回收資源。當需要執行垃圾回收時,程序會暫停一切工作(又稱為Stop The World,STW),使用復制算法完成垃圾清理工作。

優點:

  • 簡單高效,是Client模式下默認的垃圾收集器;
  • 對於資源受限的環境,比如單核(例如Docker中設置單核),單線程效率較高;
  • 內存小於一兩百兆的桌面程序中,交互有限,則有限的STW是可以接受的。

缺點:

  • 垃圾回收速度較慢且回收能力有限,頻繁的STW會導致較差的使用體驗。

ParNew收集器是Serial收集器的多線程版本,除了使用多線程進行垃圾收集工作,其他的控制參數,收集算法,對象分配規則等均與Serial收集器一致。
ParNew收集器在單核/雙核環境下,效率未必有Serial收集器工作效率高(多線程切換開銷等因素限制),當然隨着核數的增加,其性能也會得到較大的提升。

2.2 Parallel收集器

Parallel收集器(Parallel Scavenge + Parallel Old)相比於Serial收集器的主要特點是,其是通過多線程完成垃圾的清理工作。其中Parallel Scavenge使用復制算法完成垃圾收集(Parallel Old使用標記整理算法),如果從這一點看其與ParNew相似,但實際上兩者的出發點存在區別,區別如下所示:

  • ParNew出發點在於加速資源回收的速度,以減少應用的STW時間;
  • Parallel Scavenge出發點在於資源回收的吞吐量(吞吐量:用戶線程時間/(用戶線程時間 + GC線程時間)).

高吞吐量適合於交互較少的后台應用程序(諸如科學計算應用),能夠更加充分的壓榨CPU。開發者可以根據應用的實際情況,通過調整以下兩個參數追求最優性能:

  • 最大停頓時間:垃圾收集器在執行垃圾回收時終端應用執行的最大時間間隔,-XX:MaxGCPauseMills;
  • 吞吐量:執行垃圾收集的時間與執行應用的時間占比,-XX:GCTimeRatio= ,垃圾收集時間占比:1/(1+N)。

2.3 CMS收集器

CMS(Concurrent Mark Sweep)收集器是jdk 1.5推出的第一款真正意義上的並發收集器(針對老年代),實現了讓垃圾收集器與用戶線程(近似)同時工作,其具有以下特點:

  • 基於"標記-清除"算法;
  • 以獲取最短回收停頓時間為目標;
  • 並發收集,停頓時間短。

CMS的垃圾收集過程比較復雜,主要步驟如下所示:

(1) CMS Initial Mark:初始標記Root(會STW,單線程執行,不過因為僅僅把GC Roots的直接可達對象標記一下,所以速度較快);
(2) CMS Concurrent Mark:並發標記;
(3) CMS Concurrent Preclean: 並發預清理;
(4) CMS Remark: 並發標記(會STW,此步驟是因為在並發標記的過程中可能會產生新的垃圾,需要重新標記新產生的垃圾);
(5) CMS Concurrent Sweep: 並發清除;
(6) CMS Concurrent Reset: 並發重置。

以上步驟中,最為耗費時間的並發標記與並發清除階段,不需要應用程序暫停執行,所以垃圾回收的停頓時間較短。

缺點:

  • 對CPU資源敏感:並發收集雖然不會暫停應用程序,但是會占用CPU資源從而降低應用程序的執行效率(CMS默認收集線程數量=(CPU數量 + 3) / 4);
  • 產生浮動垃圾:在並發清除時,用戶線程會產生新的垃圾,稱之為浮動垃圾(並發清除時需要預留內存空間,不能像其他收集器在老年代幾乎填滿之后再進行收集工作)。
  • 產生空間碎片:使用"標記-清除"算法,會產生大量不連續的內存碎片,從而導致在分配大內存對象時,無法找到足夠的連續內存,從而需要提前觸發一次Full GC操作。

針對以上缺點,可以從如下參數進行改進:

  • -XX:ConcGCThreads:並發的GC線程數,從而降低CPU敏感度;
  • -XX:CMSInitiatingOccupancyFraction:合理設置CMS的預留內存空間;
  • -XX:+UseCMSCompactAtFullGCCollection: FullGC之后執行壓縮操作,消減內存碎片;
  • -XX:CMSFullGCBeforeCompaction: 執行多次FullGC之后執行壓縮操作,消減內存碎片。

2.4 G1收集器

需要注意的是G1垃圾收集器在新生代以及老年代都能進行工作,這是因為相比於前面所介紹的垃圾收集器,它具有不同的堆內存結構。以前的垃圾收集器分代是划分為新生代、老牛代、持久帶等

G1將內存划分為多個大小相同的Region(1-32M,上限2048個),每個Region均擁有自己的分代屬性,這些分代不需要連續。通過划分Region,G1可以根據計算老年代對象的效益率,優先回收具有最高效益率的對象(分代的內存不連續,GC搜索垃圾時需要全盤掃描找出對象引用情況,G1通過在每個Region中維護一個Remembered Set記錄對象引用情況解決此問題)。具體如下圖所示:

G1提供了兩種GC模式,Young GC以及Mixed GC,兩種GC都會STW。

2.4.1 Young GC

選定所有年輕代里的Region。通過控制年輕代的region個數,即年輕代內存大小,來控制young GC的時間開銷。

2.4.2 Mixed GC

選定所有年輕代里的Region,外加根據global concurrent marking統計得出收集收益高的若干老年代Region,在用戶指定的開銷目標范圍內盡可能選擇收益高的老年代Region。

Mixed GC不是Full GC,它只能回收部分老年代的Region,如果mixed GC實在無法跟上程序分配內存的速度,導致老年代填滿無法繼續進行Mixed GC,就會使用serial old GC(full GC)來收集整個GC heap(此時效率就會很低下)。所以我們可以知道,G1是不提供Full GC的。

在執行Mixed GC之前需要進行並發標記過程(Global Concurrent Marking),具體步驟如下圖所示:

  • Initial marking phase: 標記GCRoots(會STW);
  • Root region scanning phase: 標記存活Region;
  • Concurrent marking phase:標記存活的對象;
  • Remark phase:重新標記(會STW);
  • Cleanup phase: 回收內存。

需要注意,Mixed GC並不是一次性執行完,其會分為多個步驟執行(具體可見下一篇關於GC日志的文章)。在每次執行時,G1會計算每個Region中垃圾占內存分段比例,如果超過了-XX:G1MixedGCLiveThresholdPercent,則進行回收操作。此外,G1中可以設置堆內存中有多少空間允許浪費,即-XX:G1HeapWastePercent,在並發標記結束后,可以知道有多少空間要被回收,在每次Young GC和發生Mixed GC之前,會檢查垃圾占比是否到達了此閾值,只有到達了,才會發生Mixed GC。

PS:
資料收集過程中,感謝以下作者文章的參考:
https://bdqfork.cn/articles/33
https://juejin.im/post/5bade237e51d450ea401fd71

如果您覺得我的文章對您有幫助,請關注我的微信公眾號,謝謝!
程序員打怪之路


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM