深刻理解 JVM 之 垃圾收集器

上一篇文章中學習了 JVM 的垃圾回收機制,和內存分配和回收策略。不過這都是一些理論知識,這篇文章中會學習一下 HotSpot 虛擬機中的垃圾收集器,這都是垃圾回收理論的具體實現。html

垃圾收集器

HotSpot 虛擬機中有多種收集器,不一樣的收集器特色也不一樣,各年代使用的收集器也能夠根據應用的特色和要求進行組合。算法

Serial 收集器

Serial 收集器是一個單線程的收集器,它不只只會使用一個 CPU 或一條收集線程去完成垃圾收集工做,並且在垃圾收集時,必須暫停其餘全部的工做線程,直到它收集結束。多線程

Serial 收集器是 HotSpot 虛擬機在運行 Client 模式下的默認新生代收集器。在垃圾收集時,年輕代使用「複製」算法,老年代使用「標記-整理」算法。併發

但它也有優勢,與其餘收集器的單線程相比,因爲沒有現成交互的開銷,專心作垃圾收集,因此其簡單而高效;性能

可使用 -XX:UseSerialGC 參數選擇使用 Serial 收集器,此時年輕代採用 Serail,老年代採用 Serial Old學習

ParNew 收集器

ParNew 收集器是 Serial 收集器的多線程版本,除了使用多線程進行垃圾回收外,其餘幾乎同樣。線程

它是許多運行在 Server 模式下的虛擬機首選的新生代收集器,一個很重要的緣由是除了 Serial 收集器外,只有 ParNew 收集器與 CMS 收集器配合工做。垃圾收集時,年輕代使用「複製」算法,老年代使用「標記-整理」算法。code

ParNew 收集器默認開啓的線程數與 CPU 的數量相同,可使用 -XX:ParallelGCThreas 參數來限制垃圾收集的線程數。使用 -XX:UseParNewGC 參數來使用 ParNew 收集器。cdn

Parallel Scavenge 收集器

Parallel Scavenge 收集器是新生代收集器,也是使用複製算法的多線程收集器。與其餘收集器不一樣的是,它關注的是達到一個可控制的吞吐量,吞吐量 = 運行代碼時間 / (運行代碼時間 + 垃圾收集時間)。htm

Parallel Scanenge 收集器提供了兩個參數用於精確控制吞吐量。第一個是控制最大垃圾收停頓時間的 -XX:MaxGCPauseMillis 參數。它容許是一個大於 0 的毫秒數,收集器將盡量保證內存回收時間不超過設定值。這個參數也不是越小越好,GC 停頓時間縮短是以犧牲吞吐量和新生代空間爲代價換取的。

第二個是直接設置吞吐量大小的 -XX:GCTimeRatio 參數。它容許是一個大於 0 且小於 100 的整數,就是垃圾收集時間佔總時間的比率。

另外,Parallel Scavenge 收集器擁有自適應調節機制,它不須要手工指定新生代的大小(-Xmn)、EdenSurvivor 區的比例(-XX:SurvivorRatio)、晉升老年代對象大小 -XX:PretenureSizeThreshold 等細節參數,虛擬機會根據當前系統的運行狀況收集性能監控信息,動態調整參數以提供最合適的停頓時間及最大的吞吐量。可以使用 -XX:UseAdaptiveSizePolicy 參數來開啓。

Serial Old 收集器

Serial OldSerial 收集器的老年代版本,是一個單線程收集器,使用「標記-整理算法」。

Parallel Old 收集器

Parallel OldParallel Scavenge 收集器的老年代版本,使用多線程和「標記-整理」算法。若是新生代選擇了 Parallel Scavenge 收集器,老年代只能選擇 Serial Old 收集器。

CMS 收集器

CMS(Concurrent Mark Sweep) 是一種以獲取最短停頓時間爲目標的收集器,使用「標記-清除」算法。若是應用重視響應速度,但願停頓時間最短,就能夠選擇 CMS 收集器。

它的運做過程可分爲 4 個步驟:

  • 初始標記:僅僅標記 GC Roots 能直接關聯到的對象,速度很快,須要 Stop the world
  • 併發標記:進行 GC Roots Tracing 過程。
  • 從新標記:修正併發標記期間因用戶程序運行而致使標記產生變更的對象的標記記錄。
  • 併發清除。

CMS 的主要優勢是併發收集、低停頓。但也有三個缺點:

  • CPU 資源很是敏感。併發階段雖不會致使用戶線程停頓,但會由於佔用資源而致使程序變慢,總吞吐量下降。
  • 沒法處理浮動垃圾,也就是在標記過程後,清除階段產生但當次收集中不能處理的垃圾,可能出現 Concurrent Mode Failure 失敗而致使另外一次 Full FC 的產生。
  • CMS 基於標記-清除算法,收集結束時會產生大量空間碎片。碎片過多時,沒法找到足夠的連續空間來分配大對象,不得不提早出發一次 Full GC

可使用 -XX:UseConcMarkSweepGC 參數來選擇 CMS 收集器。

G1 收集器

G1(Garbage-First) 是一款面向服務端應用的垃圾收集器。它的特色以下:

  • 並行與併發:充分利用多 CPU、多核環境,使用多個 CPU 來縮短停頓的時間,部分須要其餘收集器本來須要停頓 Java 線程執行的 GC 動做,G1 收集器可經過併發的方式讓 Java 程序繼續執行。
  • 分代收集:G1 收集器能獨立管理整個 GC 堆,而且能採用不一樣的方式處理不一樣時期的對象。
  • 空間整合:G1 收集器從總體來看,基於「標記-整理」算法實現;從局部來看,基於「複製」算法實現。
  • 可預測的停頓:G1 能明確指定垃圾收集的限制時間。

使用 G1 收集器時,將 Java 堆劃分爲多個大小相等的區域 RegionG1 跟蹤各個 Region 的回收價值和成本(回收得到空間及回收時間),後臺會維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的 Region。它經過使用 Remembered Set 來避免全堆掃描。

G1 收集器的運行步驟可分爲:

  • 初始標記:僅僅標記一下 GC Roots 直接能關聯到的對象,須要停頓,但耗時很短。
  • 併發標記:從 GC Roots 開始對堆中對象進行可達性分析,找出存活的對象,耗時較長,但可併發執行。
  • 最終標記:修正併發標記期間因用戶程序運行而致使標記產生變更的對象的標記記錄。
  • 篩選回收:對各個 Region 的回收價值和成本進行排序,根據指定的 GC 停頓時間制定回收計劃。

經常使用收集器組合

HotSpot 虛擬機中包含了七種垃圾收集器,以下圖:

它們的組合說明以下:

新生代收集器 年老代收集器 說明
Serial Serial Old 都是單線程,GC 時會暫停全部應用線程。
使用 -XX:+UseSerialGC 選項來開啓
Serial CMS + Serial Old CMS 是併發 GC,不須要暫停全部應用線程。
當 CMS 進行 GC 失敗時,會自動使用 Serial Old 策略進行 GC
使用 -XX:+UseConcMarkSweepGC 選項來開啓
ParNew CMS ParNew 是 Serial 的並行版本,能夠指定 GC 線程數
默認 GC 線程數爲 CPU 的數量
ParNew Serial Old 使用 -XX:+UseParNewGC 選項來開啓
Parallel Scavenge Serial Old Parallel Scavenge 策略關注吞吐量,適用於後臺持久運行的應用程序
使用 -XX:+UseParallelGC 選項來開啓
Parallel Scavenge Parallel Old Parallel Old 是 Serial Old 的並行版本
使用 -XX:+UseParallelOldGC 選項來開啓
G1GC G1GC -XX:+UseG1GC #開啓
-XX:MaxGCPauseMillis #暫停時間目標

參考資料

相關文章
相關標籤/搜索