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是能夠接受的。

缺點:post

  • 垃圾回收速度較慢且回收能力有限,頻繁的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
http://www.javashuo.com/article/p-gngwroki-km.html

若是您以爲個人文章對您有幫助,請關注個人微信公衆號,謝謝!
程序員打怪之路

相關文章
相關標籤/搜索