JVM垃圾收集器專題

垃圾收集器:利用垃圾收集算法,實現垃圾回收的實踐落地。

1 HotSpot垃圾回收器

HotSpot垃圾回收器有多個,可以配合使用。

image

1.1 垃圾回收的一些術語

術語: Stop the world

簡寫爲STW,也叫全局停頓,Java代碼停止運行,native代碼繼續運行,但不能與JVM進行交互。

STW主要是爲了GC操作的準確性和效率。用戶線程不停止的話,會不斷有新對象和垃圾對象產生,假設沒有STW,會導致GC時間過長,錯誤清理新對象等情況。

原因:多半由於垃圾回收導致;也可能是Dump線程、死鎖檢查、Dump堆等導致

危害:服務停止、沒有響應主從切換(對於高可用環境,如果停頓時間過長,會引發主從之間的切換)、危害生產安全。

因此,儘量縮短Stop the world的時間

術語--並行收集 VS 併發收集

並行收集:指多個垃圾收集線程並行工作,但是在收集的過程中,用戶線程(你的業務線程)還是處於等待狀態的

併發收集:指用戶線程與垃圾收集線程同時工作

術語-- 吞吐量

CPU用於運行用戶代碼的時間與CPU總消耗時間的比值

公式:運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間)

2 新生代垃圾收集器(與老年代收集器配合使用)

2.1 Serial收集器

Serial 收集器是最基本、發展歷史最悠久的收集器,採用複製算法。採用單線程操作,收集過程中全程STW

image

適用場景有:

  • 客戶端程序,應用以-client 模式運行時,默認使用的就是Serial(java -client -jar 運行)
  • 單核機器

2.2 ParNew收集器

Serial收集器的多線程版,除使用多線程以外,其他和Serial收集器一樣,包括:JVM參數、Stop the world表現、垃圾收集算法都是一樣的。可使用 -XX:ParallelGCThreads設置垃圾收集的線程數。主要和CMS垃圾收集器配合使用。

image

2.3 ParallelScavenge收集器

ParallelScavenge收集器,是吞吐量優先收集器,也是採用複製算法,也是多線程的。執行過程與ParNew收集器類似。

Parallel Scavenge收集器適用於注重吞吐量的場景。

Parallel Scavenge收集器特點

可以達到一個可控制的吞吐量

  • -XX:MaxGCPauseMillis:控制最大的垃圾收集停頓時間(盡力)

  • -XX:GCTimeRatio:設置吞吐量的大小,取值0-100,系統花費不超過1/(1+n)的時間用於垃圾收集

自適應GC策略:可用-XX:+UseAdptiveSizePolicy打開

  • 打開自適應策略後,無需手動設置新生代的大小(-Xmn)、Eden與Survivor區的比例(-XX:SurvivorRatio)等參數虛擬機會自動根據系統的運行狀況收集性能監控信息,動態地調整這些參數,從而達到最優的停頓時間以及最高的吞吐量。

3 老年代收集器(與新生代收集器配合使用)

3.1 Serial Old收集器

Serial Old收集器是Serial收集器的老年代版本,單線程,採用的是標記-整理算法,垃圾收集過程Stop The World。

可以與上面三個新生代收集器配合使用;當CMS收集器出現故障時,作爲後備處理器。

3.2 Parallel Old收集器

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,只能與Parallel Scavenge收集器配合使用,同樣是注重對吞吐量要求較高的場景。

Parallel Old收集器,採用多線程,標記-整理算法,垃圾收集過程Stop The World。

3.3 CMS收集器(JDK9廢棄)

CMS: Concuerrent mark sweep(併發標記收集器),採用標記-清除算法。

image

CMS執行過程的七個階段:

  1. 初始標記:標記GC Roots能直接關聯的對象,觸發短暫STW操作
  2. 併發標記:找出所有GC Roots能關聯的對象。併發執行,不會觸發STW。
  3. 併發預清理階段(可選):重新標記在併發標記階段引用被更新的對象,從而減少後面重新標記的工作量。併發執行,不會觸發STW。可使用-XX:-CMSPrecleaningEnabled關閉併發預清理階段,默認打開。
  4. 併發可中止的預清理階段(可選):和併發預清理做的事情一樣,併發執行,無 Stop The World。預清理後,當Eden的使用量大於CMSScheduleEdenSizeThreshold的閾值(默認2M)時,纔會執行該階段。主要作用是允許我們能夠控制預清理階段的結束時機。
  5. 重新標記階段:修正併發標記期間,因爲用戶程序繼續運行,導致標記發生變動的那些對象的標記。一般來說,重新標記花費的時間會比初始標記階段長一些,但比並發標記的時間短。存在 Stop The World。
  6. 併發清除階段:基於標記結果,清除垃圾對象。併發執行,無STW。使用標記清除算法,因爲標記整理涉及數據位置,併發情況難以實現。
  7. 併發重置階段:清除本地CMS GC的上下文信息,爲下次GC做準備。

CMS收集器的優缺

優點:STW時間較短,大多數過程併發執行。

缺點:

  1. 垃圾回收併發執行時,會與用戶線程有一定CPU資源爭搶,影響吞吐量;
  2. CMS只在標記的時候STW,清除時無STW,無法處理浮動垃圾;
  3. 採用標記-清除法,導致內存碎片的產生;
    1. 可以使用UseCMSCompactAtFullCollection:在完成Full GC後是否要進行內存碎片整理,默認開啓。CMSFullGCsBeforeCompaction:進行幾次Full GC後就進行一次內存碎片整理,默認是0。
  4. 無法等到老年代幾乎滿了纔開始垃圾收集。
    1. 執行過程中多個階段沒有STW操作,會不斷有對象晉升到老年代,當老年代預留的內存不夠時,會導致Concurrent Mode Failure,從而切換成後備老年代收集器 Serial Old。
    2. 可使用 CMSInitiatingOccupancyFraction設置老年代佔比達到多少就觸發垃圾收集,默認68%。

CMS適用於希望系統停頓時間短,響應速度快的場景,例如Web。

擴展:併發預處理、併發中斷預處理

1、首先,CMS是一個關注停頓時間,以回收停頓時間最短爲目標的垃圾回收器。併發預處理階段做的工作是標記,重標記需要STW(Stop The World),因此重標記的工作儘可能多的在併發階段完成來減少STW的時間。此階段標記從新生代晉升的對象、新分配到老年代的對象以及在併發階段被修改了的對象。
2、併發可中斷預清理(Concurrent precleaning)是標記在併發標記階段引用發生變化的對象,如果發現對象的引用發生變化,則JVM會標記堆的這個區域爲Dirty Card。那些能夠從Dirty Card到達的對象也被標記(標記爲存活),當標記做完後,這個Dirty Card區域就會消失。CMS有兩個參數:CMSScheduleRemarkEdenSizeThreshold、CMSScheduleRemarkEdenPenetration,默認值分別是2M、50%。兩個參數組合起來的意思是預清理後,eden空間使用超過2M時啓動可中斷的併發預清理(CMS-concurrent-abortable-preclean),直到eden空間使用率達到50%時中斷,進入重新標記階段。

4 G1收集器

G1(Garbge First),面向服務端應用,可以同時用於新生代和老年代。

image

G1收集器使用Region作爲單位,包括四種類型,分別爲Eden、Survior、Old和Humongous,通過參數-XX:G1HeapRegionSize指定Region的大小,取值範圍爲1MB ~ 32 MB之間。

前三種Region仍然是伊甸園、存活區、老年代,Humongous用來存儲大對象,對象過大可以存儲到連續的Humongous中。Old和Humongous同屬於老年代。

G1收集器的設計思想將內存分成很多小塊(Region),跟蹤每個Region中垃圾堆積的價值大小,構建一個優先列表,根據允許的收集時間,優先回收價值最高的Region。其中價值大小指的是回收該Region能獲得的空間大小以及回收所需要的時間成本。

4.1 G1收集器的垃圾回收機制

主要分爲三種,Young GC、Mixed GC、Full GC。

4.1.1 Young GC

所有Eden Region都滿了的時候,就會觸發Young GC。

  • Eden中存活的對象轉移到Survior Region;
  • 原先 Survivor Region中的對象轉移到新的 Survivor Region中,或者晉升到Old Region。
  • 空閒 Region會被放入空閒列表中,等待下次被使用。

4.1.2 Mixed GC

老年代大小佔整個堆的百分比達到一定閾值(可用-XX:InitiatingHeapOccupancyPercent指定,默認45%),就觸發Mixed GC,會回收所有 Young Region,同時回收部分 Old Region。

image

執行過程分爲四步:

  1. 初始標記:跟CMS類似,標記處GC Roots能直接關聯到的對象,存在短暫STW

  2. 併發標記:跟CMS類似,找出所有GC Roots能關聯的對象。併發執行,不會觸發STW。

  3. 最終標記:更新在併發標記期間引起的變更,存在STW

  4. 篩選回收:首先對各個Region的回收價值和成本排序,根據用戶所期望的停頓時間指定回收計劃,篩選出合適的Region回收。停頓時間:MaxGCPauseMillis。

    回收過程:將Region的存活對象複製到空閒Region中,然後刪除原Region,是複製算法,無內存碎片,過程存在STW。

G1收集器Mixed GC除了併發標記以外的過程都是STW的,由於一次只回收一部分Region,所以停頓時間可控。

4.1.3 Full GC

複製對象內存不夠,或者無法分配足夠的內存(例如大對象無法分配連續的內存),就會觸發Full GC。Full GC機制採用單線程的Serial Old模式。

因此G1收集器的優化原則是,儘可能的減少Full GC。

4.2 G1優化原則:減少Full GC

  1. 增加預留內存(增大-XX:G1 Reserve Percent,默認爲堆的10%)
  2. 更早地回收垃圾(減少- XX: InitiatingHeapOccupancyPercent,老年代達到該值就觸發 Mixed GC,默認45%
  3. 增加併發階段使用的線程數(增大-XX: ConcGCThreads)

上面三點大白話總結:多留一點內存,有內存可以分配;有垃圾早點回收;垃圾回收快點。

4.3 G1收集器的使用

G1收集器作用於整個堆,可以控制停頓時間(MaxGCPauseMillis=200),並且沒有內存碎片。

G1收集器佔用內存較大(通常需要6G以上),可以代替CMS收集器。對於JDK8,主要根據內存選擇,內存小於6G,選CMS;內存大於6G,使用G1。CMS在JDK9中被廢棄,高於JDK8的版本可以選G1。

5 實驗收集器

截止目前JDK14依然處於實驗狀態

  • Shenandoah(IBM開發,是ZGC競品,進入OpenJDK,被Oracle JDK12剔除。)

  • ZGC(一款革命性的收集器)

  • Epsilon:不幹活的垃圾收集器。

    • 控制內存分配,但是不執行垃圾回收工作,堆耗盡就直接關閉JVM。

6 如何選擇垃圾收集器?

理論出發主要有下面幾點。

  1. 項目主要關注矛盾點。例如吞吐量(Parallel)、訪問延遲(CMS/G1)、應用啓動速度
  2. 基礎設施:CPU、內存等
  3. JDK版本。例如JDK6沒有G1,Oracle JDK沒有Shenandoah。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章