最全的 JVM 面試知識點(三):垃圾收集器

在涉及 Java 相關的面試中,面試官常常會讓講講 Java 中的垃圾收集相關的理解和常見的分類。可見,光就應付面試而言,JVM 的垃圾收集也對每一位 Java 開發者很重要。除此以外,對於咱們瞭解和解決 Java 應用的性能時,也頗有幫助。面試

上一篇介紹了 Java 虛擬機內存的垃圾收集算法。本章將會介紹 Java 中經常使用的垃圾收集器及其特性。算法

本文的主要內容:微信

  • 基本概念
    • 串行、並行和併發
    • JVM 垃圾收集中的串行、並行和併發
  • 串行垃圾回收器
    • Serial
    • Serial Old
  • 並行垃圾回收器
    • ParNew
    • Parallel
    • Parallel Old
    • CMS
    • G1(Garbage First)
  • 小結

基本概念

在介紹具體的垃圾回收器以前,咱們先了解幾個基本概念。多線程

串行、並行和併發

計算機系統的信息交換有兩種方式:並行數據傳輸方式和串行數據傳輸方式。閉包

  • 串行:計算機中的串行是用 Serial 表示。A 和 B 兩個任務運行在一個 CPU 線程上,在 A 任務執行完以前不能夠執行 B。即,在整個程序的運行過程當中,僅存在一個運行上下文,即一個調用棧一個堆。程序會按順序執行每一個指令。
  • 並行:並行性指兩個或兩個以上事件或活動在同一時刻發生。在多道程序環境下,並行性使多個程序同一時刻可在不一樣 CPU 上同時執行。好比,A 和 B 兩個任務能夠同時運行在不一樣的 CPU 線程上,效率較高,但受限於 CPU 線程數,若是任務數量超過了 CPU 線程數,那麼每一個線程上的任務仍然是順序執行的。
  • 併發:併發指多個線程在宏觀(相對於較長的時間區間而言)上表現爲同時執行,而其實是輪流穿插着執行,併發的實質是一個物理 CPU 在若干道程序之間多路複用,其目的是提升有限物理資源的運行效率。 併發與並行串行並非互斥的概念,若是是在一個CPU線程上啓用併發,那麼天然就仍是串行的,而若是在多個線程上啓用併發,那麼程序的執行就能夠是既併發又並行的。

舉個簡單的例子,寫代碼和聽音樂這兩件事:併發

  • 咱們在編碼的時候爲了保證注意力集中,其餘什麼也不幹。在茶歇的時候才聽音樂。
  • 咱們在編碼的時候,既能夠寫代碼,也能夠聽音樂,這兩件事同時進行。
  • 咱們會常常寫一段時間代碼就歇一下聽音樂,這兩件事穿插着進行。

讀者能夠思考上面所述的串行、並行和併發。jvm

JVM 垃圾收集中的串行、並行和併發

在 JVM 垃圾收集器中也涉及到如上的三個概念。分佈式

  • 串行(Serial):使用單線程進行垃圾回收的回收器。
  • 並行(Parallel):指多條垃圾收集線程並行工做,但此時用戶線程仍然處於等待狀態。
  • 併發(Concurrent):指用戶線程與垃圾收集線程同時執行(但不必定是並行,可能會交替執行),用戶程序在繼續運行,而垃圾收集器運行在另外一個 CPU 上。

在瞭解了這些概念以後,咱們開始具體介紹經常使用的垃圾收集器。性能

串行垃圾回收器

如上所述,串行回收器是指使用單線程進行垃圾回收的回收器,每次回收時串行回收器只有一個工做線程,對於併發能力較弱的計算機來講,串行回收器的專一性和獨佔性每每有更好的表現。串行回收器能夠在新生代和老年代使用,根據做用的堆空間不一樣,分爲新生代串行回收器和老年代串行回收器。優化

Serial

Serial收集器是最古老的收集器,它的缺點是當Serial收集器想進行垃圾回收的時候,必須暫停用戶的全部進程,即 STW(服務暫停)。到如今爲止,它依然是虛擬機運行在 client 模式下的默認新生代收集器。

參數控制:-XX:+UseSerialGC 使用串行收集器。

Serial Old

Serial 收集器的老年代版本,它一樣是一個單線程收集器。它主要有兩大用途:一種用途是在 JDK1.5 以及之前的版本中與 Parallel Scavenge 收集器搭配使用,另外一種用途是做爲 CMS 收集器的後備方案。

UseSerialGC:開啓此參數使用 Serial & Serial Old 蒐集器(client 模式默認值)。

並行垃圾回收器

並行回收器是在串行回收器的基礎上作了改進,它可使用多個線程同時進行垃圾回收,對於計算能力強的計算機來講,能夠有效的縮短垃圾回收所需的實際時間。

ParNew

ParNew 收集器是一個工做在新生代的垃圾收集器,它只是簡單的將串行收集器多線程化,它的回收策略和算法和串行回收器同樣。新生代並行,老年代串行;新生代複製算法、老年代標記-整理。

參數控制:-XX:+UseParNewGC 使用 ParNew 收集器;-XX:ParallelGCThreads 限制線程數量 除了 Serial 收集器外,只有它能與 CMS 收集器(真正意義上的併發收集器,後面會介紹到)配合工做。

Parallel

Parallel 是採用複製算法的多線程新生代垃圾回收器,Parallel 收集器更關注系統的吞吐量。所謂吞吐量就是 CPU 用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間/(運行用戶代碼時間 + 垃圾收集時間)。

停頓時間越短就越適合須要與用戶交互的程序,良好的響應速度可以提高用戶的體驗;

而高吞吐量則能夠最高效率地利用CPU時間,儘快地完成程序的運算任務,主要適合在後臺運算而不須要太多交互的任務。

能夠經過參數來打開自適應調節策略,虛擬機會根據當前系統的運行狀況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或最大的吞吐量;也能夠經過參數控制GC的時間不大於多少毫秒或者比例;新生代複製算法、老年代標記-整理

參數控制:

  • -XX:MaxGCPauseMillis 設置最大垃圾收集停頓時間
  • -XX:GCTimeRatio 設置吞吐量的大小(默認是99)
  • -XX:+UseAdaptiveSeizPolicy 打開自適應模式,當這個參數打開以後,就不須要手工指定新生代的大小、Eden與Survivor區的比例、晉升老年代對象年齡等細節參數了,虛擬機會根據當前系統的運行狀況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者最大的吞吐量

Parallel Old

Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本,採用多線程和標記-整理算法,也是比較關注吞吐量。在注重吞吐量及 CPU 資源敏感的場合,均可以優先考慮 Parallel 加 Parallel Old 收集器。

參數控制:-XX:+UseParallelOldGC 使用 Parallel Old 收集器;-XX:ParallelGCThreads 限制線程數量。

CMS 垃圾回收器

CMS(Concurrent Mark Sweep) 併發標記請除,它使用的是標記-清除法,工做在老年代,主要關注系統的停頓時間。

CMS 並非獨佔的回收器,也就是說,CMS 回收的過程當中應用程序仍然在不停的工做,又會有新的垃圾不斷的產生,因此在使用CMS的過程當中應該確保應用程序的內存足夠可用,CMS不會等到應用程序飽和的時候纔去回收垃圾,而是在某一閥值(默認爲68)的時候開始回收,也就是說當老年代的空間使用率達到68%的時候會執行CMS。若是內存使用率增加很快,在CMS執行過程當中,已經出現了內存不足的狀況,此時,CMS回收就會失敗,虛擬機將啓動老年代 Serial 進行垃圾回收,這會致使應用程序中斷,直到垃圾回收完成後纔會正常工做,這個過程GC的停頓時間可能較長,因此閥值的設置要根據實際狀況設置。

  • 初始標記:暫停全部的其餘線程,並記錄下直接與root相連的對象,速度很快;
  • 併發標記:同時開啓GC和用戶線程,用一個閉包結構去記錄可達對象。但在這個階段結束,這個閉包結構並不能保證包含當前全部的可達對象。由於用戶線程可能會不斷的更新引用域,GC線程沒法保證可達性分析的實時性。因此這個算法裏會跟蹤記錄這些發生引用更新的地方。
  • 從新標記:從新標記階段就是爲了修正併發標記期間由於用戶程序繼續運行而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段的時間稍長,遠遠比並發標記階段時間短
  • 併發清除:開啓用戶線程,同時GC線程開始對未標記的區域作清掃。

主要優勢:併發收集、低停頓。可是它有下面三個明顯的缺點:

  • 對CPU資源敏感;
  • 沒法處理浮動垃圾;
  • 使用的 標記-清除 算法會致使收集結束時會有大量空間碎片產生。

標記清除法產生的內存碎片問題,CMS 提供提供了一些優化設置,能夠設置完成 CMS 以後進行一次碎片整理,也能夠設置進行多少次 CMS 回收後進行碎片整理。

參數控制:

  • -XX:+UserConcMarkSweepGC 使用 CMS 垃圾清理器
  • -XX:CMSInitatingPermOccupancyFraction 設置閥值
  • -XX:ConcGCThreads 限制線程數量
  • -XX:+UseCMSCompactAtFullCollection 設置完成 CMS 以後進行一次碎片整理
  • -XX:CMSFullGCsBeforeCompaction 設置進行多少次 CMS 回收後進行碎片整理

G1(Garbage First)

G1(Garbage First) 垃圾收集器是當今垃圾回收技術最前沿的成果之一。早在 JDK7 就已加入 JVM 的收集器你們庭中,成爲 HotSpot 重點發展的垃圾回收技術。

G1 收集器在後臺維護了一個優先列表,每次根據容許的收集時間,優先選擇回收價值最大的 Region。包括:Eden、Survivor、Old 和 Humongous。

其中,Humongous 是特殊的 Old 類型,回收空閒巨型分區,專門放置大型對象。這樣的劃分方式意味着不須要一個連續的內存空間管理對象。G1 將空間分爲多個區域,優先回收垃圾最多的區域。一個對象和它內部所引用的對象可能不在同一個 Region 中,那麼當垃圾回收時,是否須要掃描整個堆內存才能完整地進行一次可達性分析?

固然不是,每一個 Region 都有一個 Remembered Set(已記憶集合),用於記錄本區域中全部對象引用的對象所在的區域,從而在進行可達性分析時,只要在 GC Roots 中再加上 Remembered Set 便可防止對全部堆內存的遍歷。

同 CMS 垃圾回收器同樣,G1 也是關注最小時延的垃圾回收器,也一樣適合大尺寸堆內存的垃圾收集,官方也推薦使用 G1 來代替選擇 CMS。G1 最大的特色是引入分區的思路,弱化了分代的概念,合理利用垃圾收集各個週期的資源,解決了其餘收集器甚至 CMS 的衆多缺陷。

G1收集器的運做大體分爲如下幾個步驟:

  • 初始標記:初始標記階段僅僅只是標記一下 GC Roots 能直接關聯到的對象,而且修改 TAMS 的值,讓下一個階段用戶程序併發運行時,能在正確可用的 Region 中建立新對象,這一階段須要停頓線程,可是耗時很短。
  • 併發標記:併發標記階段是從 GC Root 開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶程序併發執行。
  • 最終標記:而最終標記階段則是爲了修正在併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程 Remenbered Set Logs 裏面,最終標記階段須要把Remembered Set Logs 的數據合併到 Remembered Set Logs 裏面,最終標記階段須要把 Remembered Set Logs 的數據合併到 Remembered Set 中,這一階段須要停頓線程,可是可並行執行。
  • 篩選回收:最後在篩選回收階段首先對各個Region的回收價值和成本進行排序,根據用戶所指望的 GC 停頓時間來制定回收計劃。

G1 能充分利用多 CPU、多核環境下的硬件優點,使用多 CPU(CPU 或者 CPU 核心)來縮短 Stop-The-World 停頓的時間,部分其餘收集器本來須要停頓 Java 線程執行的GC動做,G1 收集器仍然能夠經過併發的方式讓 Java 程序繼續執行。

此外,與其餘收集器同樣,分代概念在G1中依然得以保留。雖然 G1 能夠不需其餘收集器配合就能獨立管理整個 GC 堆,但它可以採用不一樣的方式去處理新建立的對象和已經存活了一段時間、熬過屢次 GC 的舊對象以獲取更好的收集效果。

空間整合:與 CMS 的 標記-清理 算法不一樣,G1 從總體看來是基於 標記-整理 算法實現的收集器,從局部(兩個 Region 之間)上看是基於 複製 算法實現,不管如何,這兩種算法都意味着 G1 運做期間不會產生內存空間碎片,收集後能提供規整的可用內存。這種特性有利於程序長時間運行,分配大對象時不會由於沒法找到連續內存空間而提早觸發下一次 GC。

可預測的停頓:這是 G1 相對於 CMS 的另一大優點,下降停頓時間是 G1 和 CMS 共同的關注點,但 G1 除了追求低停頓外,還能創建可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M毫秒的時間片斷內,消耗在垃圾收集上的時間不得超過 N 毫秒,這幾乎已是實時 Java(RTSJ)的垃圾收集器特徵了。

參數控制:-XX:+UseG1GC

小結

本文介紹了常見的7種不一樣分代的收集器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;而它們所處區域,則代表其是屬於新生代收集器仍是老年代收集器:

  • 新生代收集器:Serial、ParNew、Parallel Scavenge;
  • 老年代收集器:Serial Old、Parallel Old、CMS;
  • 整堆收集器:G1;

根據收集的區域(年輕代或年老代)和收集器自身的特性,能夠有以下組合: Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel/Serial Old、Parallel/Parallel Old、G1。

ZGC 來了。ZGC 是 JDK11 中要發佈的最新垃圾收集器。徹底沒有分代的概念,官方給出 ZGC 的優勢是無碎片,時間可控,超大堆。讀者能夠嘗試瞭解和使用一下 ZGC 。

推薦閱讀

最全的 JVM 面試知識點系列文章

訂閱最新文章,歡迎關注個人公衆號

微信公衆號

參考

  1. 從串行到並行,從並行到分佈式
  2. 詳解 JVM Garbage First(G1) 垃圾收集器
相關文章
相關標籤/搜索