垃圾收集3: 垃圾回收器

若是說收集算法是內存回收的方法論,那麼垃圾收集器就是內存回收的具體實現。如今爲止尚未最好的垃圾收集器出現,更加沒有萬能的垃圾收集器,咱們能作的就是根據具體應用場景選擇適合本身的垃圾收集器。HotSpot虛擬機中的7個垃圾收集器以下所示:java

若是兩個收集器存在連線,說明能夠搭配使用。所處的區域表示屬於新生代仍是老年代收集器。

  • 單線程與多線程:單線程指的是垃圾收集器只使用一個線程進行收集,而多線程使用多個線程;
  • 串行與並行:串行指的是垃圾收集器與用戶程序交替執行,這意味着在執行垃圾收集的時候須要停頓用戶程序;並行指的是垃圾收集器和用戶程序同時執行。除了 CMS 和 G1 以外,其它垃圾收集器都是以串行的方式執行。

Serial 收集器

以串行的方式執行,是單線程的收集器,只會使用一個線程來進行垃圾收集工做。

新生代採用複製算法,老年代採用標記-整理算法。git

它的優勢是簡單高效,對於單個 CPU 環境來講,因爲沒有線程交互的開銷,所以擁有最高的單線程收集效率。github

它是Client模式下的默認新生代收集器,由於在該應用場景下,分配給虛擬機管理的內存通常來講不會很大。Serial 收集器收集幾十兆甚至一兩百兆的新生代停頓時間能夠控制在一百多毫秒之內,只要不是太頻繁,這點停頓是能夠接受的。算法

ParNew收集器

是Serial收集器的多線程版本。除了使用多線程進行垃圾收集外,其他行爲(控制參數、收集算法、回收策略等等)和Serial收集器徹底同樣。

新生代採用複製算法,老年代採用標記-整理算法。服務器

Server模式下的虛擬機首選新生代收集器,除了性能緣由外,主要是由於除了 Serial 收集器,只有它能與 CMS 收集器配合工做。多線程

在單CPU的環境中不會有比Serial收集器更好的效果。可是隨着使用的CPU的數量的增長,對於GC時系統資源的有效利用是有好處的。默認開啓的線程數量與 CPU 數量相同,可使用 -XX:ParallelGCThreads 參數來設置線程數。閉包

並行:多條垃圾收集線程並行工做,單此時用戶線程仍然處於等待狀態。併發

併發:用戶線程與垃圾收集線程同時執行,不必定是並行的。用戶程序在繼續運行,GC程序運行在另一個CPU上。jvm

Parallel Scavenge收集器

相似於ParNew收集器,是一個並行的多線程收集器、新生代收集器,使用複製算法。性能

其它收集器關注點是儘量縮短垃圾收集時用戶線程的停頓時間,而它的目標是達到一個可控制的吞吐量,它被稱爲「吞吐量優先」收集器。這裏的吞吐量指 CPU 用於運行用戶代碼的時間佔總時間的比值。

停頓時間越短就越適合須要與用戶交互的程序,良好的響應速度能提高用戶體驗。而高吞吐量則能夠高效率地利用 CPU 時間,儘快完成程序的運算任務,適合在後臺運算而不須要太多交互的任務。

縮短停頓時間是以犧牲吞吐量和新生代空間來換取的:新生代空間變小,垃圾回收變得頻繁,致使吞吐量降低。

Serial Old收集器

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

Parallel Old收集器

是 Parallel Scavenge 收集器的老年代版本,使用多線程和「標記-整理」算法。

在注重吞吐量以及 CPU 資源敏感的場合,均可以優先考慮 Parallel Scavenge 加 Parallel Old 收集器。

CMS收集器

CMS(Concurrent Mark Sweep),Mark Sweep指的是標記-清除算法。

是一種以獲取最短回收停頓時間爲目標的收集器。於是很是符合在注重用戶體驗的應用上使用。

是HotSpot虛擬機第一款真正意義上的併發收集器,它第一次實現了讓垃圾收集線程與用戶線程(基本上)同時工做。

分爲如下四個流程:

  1. 初始標記:僅僅只是標記如下GC Roots能直接關聯到的對象。速度很快,須要停頓。
  2. 併發標記:進行GC Root Tracing的過程,同時開啓GC和用戶線程,用一個閉包結構去記錄可達對象。但在這個階段結束,這個閉包結構並不能保證包含當前全部的可達對象。由於用戶線程可能會不斷的更新引用域,因此GC線程沒法保證可達性分析的實時性。因此這個算法裏會跟蹤記錄這些發生引用更新的地方。在整個回收過程當中耗時最長,不須要停頓。
  3. 從新標記:爲了修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長,但遠比並發標記的時間段。
  4. 併發清除: 開啓用戶線程,同時GC線程開始對爲標記的區域作清掃。

主要優勢:併發收集、低停頓

具備如下三個缺點:

  • 對CPU資源敏感,吞吐量低。併發階段雖然不會致使用戶線程停頓,可是會由於佔用了一部分CPU資源致使引用程序變慢,總吞吐量下降。
  • 沒法處理浮動垃圾,可能出現Concurrent Mode Failure。浮動垃圾是指併發清除階段因爲用戶線程繼續運行而產生的垃圾,這部分垃圾只能到下一次 GC 時才能進行回收。因爲浮動垃圾的存在,所以須要預留出一部份內存,意味着 CMS 收集不能像其它收集器那樣等待老年代快滿的時候再回收。若是預留的內存不夠存放浮動垃圾,就會出現 Concurrent Mode Failure,這時虛擬機將臨時啓用 Serial Old 來替代 CMS。
  • 使用「標記-清除」算法會致使收集結束時有大量空間碎片產生。每每出現老年代空間剩餘,但沒法找到足夠大連續空間來分配當前對象,不得不提早觸發一次 Full GC。

G1收集器

G1 (Garbage-First)是一款面向服務器的垃圾收集器,主要針對配備多顆處理器及大容量內存的機器,在多 CPU 和大內存的場景下有很好的性能,以極高機率知足GC停頓時間要求的同時,還具有高吞吐量性能特徵,被視爲JDK1.7中HotSpot虛擬機的一個重要進化特徵。HotSpot 開發團隊賦予它的使命是將來能夠替換掉 CMS 收集器。

具備如下特色:

  • 並行與併發:G1能充分利用CPU、多核環境下的硬件優點,使用多個CPU(CPU或者CPU核心)來縮短Stop-The-World停頓時間。部分其餘收集器本來須要停頓Java線程執行的GC動做,G1收集器仍然能夠經過併發的方式讓java程序繼續執行。
  • 分代收集:雖然G1能夠不須要其餘收集器配合就能獨立管理整個GC堆,可是仍是保留了分代的概念。
  • 空間整合:與CMS的「標記--清理」算法不一樣,G1從總體來看是基於「標記整理」算法實現的收集器;從局部上來看是基於「複製」算法實現的,這意味着運行期間不會產生內存空間碎片。
  • 可預測的停頓:這是G1相對於CMS的另外一個大優點,下降停頓時間是G1 和 CMS 共同的關注點,但G1 除了追求低停頓外,還能創建可預測的停頓時間模型,能讓使用者明確指定在一個長度爲 M 毫秒的時間片斷內,消耗在 GC 上的時間不得超過 N 毫秒。

其它收集器進行收集的範圍都是整個新生代或者老年代,而 G1 能夠直接對新生代和老年代一塊兒回收。

G1 把堆劃分紅多個大小相等的獨立區域(Region),新生代和老年代再也不物理隔離。

經過引入 Region 的概念,從而將原來的一整塊內存空間劃分紅多個的小空間,使得每一個小空間能夠單獨進行垃圾回收。這種劃分方法帶來了很大的靈活性,使得可預測的停頓時間模型成爲可能。經過記錄每一個Region垃圾回收時間以及回收所得到的空間(這兩個值是經過過去回收的經驗得到),並維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的 Region。

每一個 Region 都有一個 Remembered Set,用來記錄該 Region 對象的引用對象所在的 Region。經過使用 Remembered Set,在作可達性分析的時候就能夠避免全堆掃描。

若是不計算維護 Remembered Set 的操做,G1 收集器的運做大體可劃分爲如下幾個步驟:

  • 初始標記:標記GC Roots能直接關聯的對象,須要停頓,時間短
  • 併發標記:從GC Root開始對堆中對象進行可達性分析,耗時長
  • 最終標記:爲了修正在併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程的 Remembered Set Logs 裏面,最終標記階段須要把 Remembered Set Logs 的數據合併到 Remembered Set 中。這階段須要停頓線程,可是可並行執行。
  • 篩選回收:首先對各個 Region 中的回收價值和成本進行排序,根據用戶所指望的 GC 停頓時間來制定回收計劃。此階段其實也能夠作到與用戶程序一塊兒併發執行,可是由於只回收一部分 Region,時間是用戶可控制的,並且停頓用戶線程將大幅度提升收集效率。

參考資料

相關文章
相關標籤/搜索