Java虛擬機04——垃圾收集器

主要介紹HotSpot虛擬機的垃圾收集器,這個虛擬機包含的全部收集器如圖所示:算法

image.png

能夠看到,收集器之間是能夠搭配使用的。下面介紹這些收集器的特性、基本原理和使用場景。在介紹以前先明確一個觀點:直到如今爲止尚未最好的收集器出現,更加沒有萬能的收集器,選擇的是對具體應用最合適的收集器。多線程

串行收集器

串行收集器是最基本、發展歷史最悠久的收集器。它們的特色就是單線程運行及獨佔式運行,所以會帶來很很差的用戶體驗。雖然它的收集方式對程序的運行並不友好,但因爲它的單線程執行特性,應用於單個CPU硬件平臺的性能能夠超過其餘的並行或併發處理器。併發

Serial收集器

  • 在JDk1.3以前是新生代收集的惟一選擇
  • 單線程,只會使用一個CPU去完成
  • 垃圾收集時,必須暫停其餘工做線程,直到它收集結束。「Stop The World」
  • 虛擬機後臺自動發起和自動完成的,在用戶不可見的狀況下把用戶正常工做的線程所有停掉
  • 到如今爲止,是虛擬機運行在Client模式下的默認新生代收集器

經過JVM參數-XX:+UseSerialGC可使用串行垃圾回收器。性能

Serial Old收集器

  • 是Serial收集器的老年代版本,是單線程收集器,採用「標記-整理算法」
  • 主要意義也是在於給Client模式下的虛擬機使用
  • 若是在Server模式下,那麼它主要還有兩大用途:
  •   在JDK 1.5以及以前的版本中與Parallel Scavenge收集器搭配使用;
  •   做爲CMS收集器的後備預案,在併發收集發生Concurrent Mode Failure時使用。這兩點都將在後面的內容中詳細講解。

Serial與Serial Old工做過程如圖: 線程

image.png

要啓用老年代串行收集器,能夠嘗試使用下面的參數:3d

  • -XX:+UseSerialGC:新生代和老年代都是用串行收集器;
  • -XX:+UseParNewGC:新生代使用ParNew收集器,老年代使用串行收集器;
  • -XX:+UseParallelGC:新生代使用Parallel GC收集器,老年代使用串行收集器。

並行收集器

並行收集器是多線程的收集器,在多核CPU下可以很好的提升收集性能。cdn

ParNew收集器

ParNew收集器是Serial收集器的多線程版本。除了使用多條線程進行垃圾收集以外,其他行爲如全部控制參數(例如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、對象分配規則、回收策略等都與Serial收集器徹底同樣,在實現上,這兩種收集器也共用了至關多的代碼。ParNew收集器的工做過程以下圖所示:對象

image.png

  • 運行在Server模式下的虛擬機中首選的新生代收集器,由於除了Serial收集器外,目前只有它能與CMS收集器配合工做
  • 因爲存在線程交互的開銷,ParNew收集器在單CPU環境下性能並無單線程垃圾收集器性能好。
  • 可使用-XX:ParallelGCThreads參數來限制垃圾收集的線程數。通常來講,當CPU數量小於8個時,宜設置爲CPU數量;當CPU數量大於8個時,宜設置爲3 + (( 5 * CPU_Count ) / 8 )。

開啓ParNew收集器可使用如下參數:blog

  • -XX:+UseParNewGC:新生代使用ParNew收集器,老年代使用串行收集器。
  • -XX:+UseConcMarkSweepGC:新生代使用ParNew收集器,老年代使用CMS收集器。

ParNew是並行的收集器,在這裏介紹一下並行與併發的概念內存

  • 並行(Parallel):多條收集器線程並行工做,但此時用戶線程仍然處於等待狀態。(合做)
  • 併發(Concurrent):用戶線程與垃圾收集器線程同時執行,不必定是並行執行,多是交替執行(競爭)

Parallel Scavenge收集器

Parallel Scavenge收集器與ParNew收集器相似,也是使用複製算法的並行的多線程新生代收集器。但Parallel Scavenge收集器關注可控制的吞吐量(Throughput)

注:吞吐量是指CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間 /( 運行用戶代碼時間 + 垃圾收集時間 )

Parallel Scavenge收集器提供了兩個參數用於精確控制吞吐量:

  • -XX:MaxGCPauseMillis:最大垃圾收集停頓時間,是一個大於0的毫秒數,收集器將回收時間儘可能控制在這個設定值以內;但須要注意的是在一樣的狀況下,回收時間與回收次數是成反比的,回收時間越小,相應的回收次數就會增多。因此這個值並非越小越好。
  • -XX:GCTimeRatio:吞吐量大小,是一個(0, 100)之間的整數,表示垃圾收集時間佔總時間的比率。

除上述兩個參數以外,Parallel Scavenge收集器還提供了一個參數-XX:+UseAdaptiveSizePolicy,當這個參數打開以後,就不須要手工指定新生代的大小(-Xmn)、Eden與Survivor區的比例(-XX:SurvivorRatio)、晉升老年代對象年齡(-XX:PretenureSizeThreshold)等細節參數了,虛擬機會根據當前系統的運行狀況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者最大的吞吐量,這種調節方式稱爲GC自適應的調節策略(GC Ergonomics)。自適應調節策略也是Parallel Scavenge收集器與ParNew收集器的一個重要區別。

Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和「標記-整理」算法。這個收集器是在JDK 1.6中才開始提供的。

因爲若是新生代選擇了Parallel Scavenge收集器,老年代除了Serial Old(PS MarkSweep)收集器外別無選擇(Parallel Scavenge沒法與CMS收集器配合工做),Parallel Old收集器的出現就是爲了解決這個問題。Parallel Scavenge和Parallel Old收集器的組合更適用於注重吞吐量以及CPU資源敏感的場合。Parallel Old收集器的工做過程下圖所示:

image.png

CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器,也是基於「標記—清除」算法實現的,它的運做整個過程過程細分爲4個步驟,包括:

  • 初始標記(CMS initial mark):須要「Stop The World」,標記一下GC Roots能直接關聯到的對象,速度很快。
  • 併發標記(CMS concurrent mark):進行GC Roots Tracing的過程,標記老年代全部存活對象。
  • 從新標記(CMS remark):須要「Stop The World」,修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長一些,但遠比並發標記的時間短。
  • 併發清除(CMS concurrent sweep):清理垃圾。

因爲整個過程當中耗時最長的併發標記和併發清除過程當中,收集器線程均可以與用戶線程一塊兒工做,因此整體上來講,CMS收集器的內存回收過程是與用戶線程一塊兒併發地執行,如圖:

image.png

CMS有如下3個明顯的缺點:

  • CMS收集器對CPU資源很是敏感。CMS默認啓動的回收線程數是( CPU Count + 3 ) / 4,當CPU在4個以上時,併發回收時垃圾收集線程很多於25%的CPU資源,而且隨着CPU數量的增長而降低。可是當CPU不足4個(譬如2個)時,將分出一半的運算能力去執行收集器線程,就可能致使用戶程序的執行速度突然下降了50%。

  • CMS收集器沒法處理浮動垃圾(Floating Garbage),可能出現「Concurrent Mode Failure」失敗而致使另外一次Full GC的產生。浮動垃圾是指CMS在併發清理階段用戶線程還在同時執行時產生的垃圾。因爲在垃圾收集階段用戶線程還須要運行,還須要預留有足夠的內存空間給用戶線程使用,所以須要預留一部分空間提供併發收集時的程序運做使用。在JDK 1.5的默認設置下,CMS收集器當老年代使用了68%的空間後就會被激活,能夠適當調高參數-XX:CMSInitiatingOccupancyFraction的值來提升觸發百分比,以下降內存回收次數;在JDK 1.6中,CMS收集器的啓動閾值已經提高至92%。若是運行期間預留的內存沒法知足程序須要,就會出現一次「Concurrent Mode Failure」失敗,這時虛擬機將臨時啓用Serial Old收集器來從新進行老年代的垃圾收集,致使停頓時間就很長了。因此說參數-XX:CMSInitiatingOccupancyFraction設置得過高很容易致使大量「Concurrent Mode Failure」失敗,性能反而下降。

  • CMS是一款基於「標記—清除」算法實現的收集器,收集結束時會有大量空間碎片產生。空間碎片過多沒法找到足夠大的連續空間來分配當前對象,不得不提早觸發一次Full GC。爲了解決這個問題,CMS收集器提供了一個-XX:+UseCMSCompactAtFullCollection開關參數(默認就是開啓的),用於在CMS收集器要進行Full GC時開啓內存碎片的合併整理過程,內存整理的過程是沒法併發的,所以也會致使停頓時間變長。而另一個參數-XX:CMSFullGCsBeforeCompaction能夠設置執行多少次不壓縮的Full GC後,才執行一次帶壓縮的(默認值爲0,即每次進入Full GC時都進行碎片整理)。

G1收集器

G1是一款面向服務端應用的垃圾收集器,與其餘GC收集器相比,G1具有以下特色:

  • 並行與併發:G1能充分利用多CPU、多核環境下的硬件優點,使用多個CPU(CPU或者CPU核心)來縮短Stop-The-World停頓的時間。
  • 分代收集:分代概念在G1中依然保留。G1能夠獨立管理整個GC堆,且採用不一樣的方式去處理分代對象。
  • 空間整合:G1從總體來看是基於「標記—整理」算法實現的,從局部(兩個Region之間)上來看是基於「複製」算法實現的;G1收集後能提供規整的可用內存。 可預測的停頓:G1能創建可預測的停頓時間模型,能明確指定垃圾收集相對於時間段的吞吐量。
  • G1收集器將整個Java堆劃分爲多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代再也不是物理隔離的了,它們都是一部分Region(不須要連續)的集合。

G1根據各個Region回收所得到的空間大小以及回收所需時間等指標在後臺維護一個優先列表,每次根據容許的收集時間,優先回收價值最大的Region,從而能夠有計劃地避免在整個Java堆中進行全區域的垃圾收集。

G1收集器運做步驟以下:

  • 初始標記(Initial Marking):僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,須要「Stop The World」。(OopMap)
  • 併發標記(Concurrent Marking): 進行GC Roots Tracing的過程
  • 最終標記(Final Marking): 修正併發標記期間因用戶程序繼續運做而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段稍長一些,但遠比並發標記的時間短,也須要「Stop The World」。(修正Remebered Set)
  • 篩選回收(Live Data Counting and Evacuation): 首先對各個Region的回收價值和成本進行排行,根據用戶所指望的GC停頓時間來制定回收計劃,這個階段其實也能夠作到與用戶程序一塊兒併發執行,可是由於只回收一部分Region,時間是用戶可控制的,並且停頓用戶線程將大幅提升收集效率。

image.png

垃圾收集參數總結

參數 描述
UseSerialGC 虛擬機運行在Client模式下的默認值,打開此開關後,使用 Serial+Serial Old 的收集器組合進行內存回收
UseParNewGC 打開此開關後,使用 ParNew + Serial Old 的收集器組合進行內存回收
UseConcMarkSweepGC 打開此開關後,使用 ParNew + CMS + Serial Old 的收集器組合進行內存回收。Serial Old 收集器將做爲 CMS 收集器出現 Concurrent Mode Failure 失敗後的後備收集器使用
UseParallelGC 虛擬機運行在 Server 模式下的默認值,打開此開關後,使用 Parallel Scavenge + Serial Old(PS MarkSweep) 的收集器組合進行內存回收
UseParallelOldGC 打開此開關後,使用 Parallel Scavenge + Parallel Old 的收集器組合進行內存回收
SurvivorRatio 新生代中 Eden 區域與 Survivor 區域的容量比值,默認爲8,表明 Eden : Survivor = 8 : 1
PretenureSizeThreshold 直接晉升到老年代的對象大小,設置這個參數後,大於這個參數的對象將直接在老年代分配
MaxTenuringThreshold 晉升到老年代的對象年齡,每一個對象在堅持過一次 Minor GC 以後,年齡就增長1,當超過這個參數值時就進入老年代
UseAdaptiveSizePolicy 動態調整 Java 堆中各個區域的大小以及進入老年代的年齡
HandlePromotionFailure 是否容許分配擔保失敗,即老年代的剩餘空間不足以應付新生代的整個 Eden 和 Survivor 區的全部對象都存活的極端狀況
ParallelGCThreads 設置並行GC時進行內存回收的線程數
GCTimeRatio GC 時間佔總時間的比率,默認值爲99,即容許 1% 的GC時間,僅在使用 Parallel Scavenge 收集器生效
MaxGCPauseMillis 設置 GC 的最大停頓時間,僅在使用 Parallel Scavenge 收集器時生效
CMSInitiatingOccupancyFraction 設置 CMS 收集器在老年代空間被使用多少後觸發垃圾收集,默認值爲 68%,僅在使用 CMS 收集器時生效
UseCMSCompactAtFullCollection 設置 CMS 收集器在完成垃圾收集後是否要進行一次內存碎片整理,僅在使用 CMS 收集器時生效
CMSFullGCsBeforeCompaction 設置 CMS 收集器在進行若干次垃圾收集後再啓動一次內存碎片整理,僅在使用 CMS 收集器時生效
相關文章
相關標籤/搜索