JVM GC系列 — GC收集器

一.前言

前文學習了各類GC回收算法,掌握了GC回收的原理,可是真正的GC實現卻尤其複雜,本篇文章將主要介紹各類GC收集器。算法

目前主流的HotSpot VM支持多種虛擬機,這些虛擬機也體現了GC的發展歷程,從單線程GC到多線程GC,分代GC到G1 GC。緩存

本文主要從如下幾個方面介紹GC收集器:多線程

  • 各類GC的特色
  • GC匹配和參數使用
  • GC日誌格式
  • 經常使用的GC參數總結


二.各類GC的特色

HotSpot中採用分代GC,從早期的單線程串行Garbage Collector到後面的多線程並行Garbage Collecot,衍生出了不少款Collector。併發

其中負責收集年輕代:oracle

  1. Serial:單線程串行收集器,使用複製算法回收年輕代
  2. ParNew:多線程並行收集器,使用複製算法回收年輕代
  3. Parallel Scavenge:多線程並行收集器,使用複製算法回收年輕代

其中負責收集老年代:框架

  1. Serial Old:相似Serial,單線程串行收集器,使用標記整理算法回收老年代
  2. CMS:併發標記整理的收集器,使用標記清除算法回收老年代
  3. Parallel Old:相似ParNew和Parallel Scavenge,使用標記整理算法回收老年代

1.Serial收集器

Serial收集器是一款很是古老的收集器,它使用單線程串行方式回收年輕代,會產生STW。post

Note:
STW即Stop The World,即中止全部用戶線程,只有GC線程在運行。學習

每次進行GC時,首先停頓全部的用戶線程,而後只有一個GC線程回收年輕代中的死亡對象。在Java Client模式中,默認任然使用Serial,由於Client模式主要針對桌面應用,通常內存較小,在百M範圍內,使用單線程收集Serial效率很是高,能夠帶來不多時間的停頓,用戶體檢很是好。spa

2.ParNew收集器

在早期,只有單線程收集器時,年輕代別無選擇。後續又演變成多線程GC年輕代,便衍生出ParNew這款並行收集器,它的並行實現主要是在GC期間使用多線程回收年輕代。線程

這款並行收集器在GC期間,也須要STW。通常多數用於Server端的年輕代GC。

3.Parallel Scavenge收集器

顧名思義,這個款年輕代收集器也是並行收集器,和ParNew的功能差很少,一樣適用複製算法。可是它更注重系統運行的吞吐量。這裏說的吞吐量,指的是CPU用於運行應用程序的時間和CPU總時間的佔比,吞吐量 = 代碼運行時間 / (代碼運行時間 + 垃圾收集時間)。可是它的來源比較奇葩,沒有遵循GC框架,致使和CMS不能兼容。關於這點能夠參考:

ParNew 和 PSYoungGen 和 DefNew 是一個東西麼?

4.Serial Old收集器

該收集器和Serial收集器的功能同樣,都是單線程串行收集器,GC期間也會STW。可是它用於收集老年代且使用了標記整理算法,這兩點它和Serial收集器不同。主要也是應用在Client模式下的桌面應用中。

5.Parallel Old收集器

Parallel Old和ParNew和Parallel Scavenge相似,是一款老年代的多線程並行收集器。通常只配合Parallel Scavenge使用。

6.CMS收集器

CMS(Concurrent Mark Sweep 併發標記清理)收集器是平常應用中最常被使用的收集器。它主要是爲了減小停頓的時間,下降延遲而生,多應用對實時性要求比較的應用場景,如:互聯網應用。

它主要分爲四個過程:

  1. 初始標記:這階段將標記不可達對象,標記階段將暫停全部用戶線程,形成STW。可是這階段只是標記出GC Roots,停頓時間相對較短。
  2. 併發標記:這階段GC線程將會和用戶線程同時運行,將從初始階段標記出的GC Roots出發標記老年代全部對象
  3. 從新標記:這階段將暫停所用用戶線程,形成STW。可是一樣相對較短,主要是爲了從新標記出在併發階段發生引用變化的對象,由於併發標記階段是和用戶線程併發運行,可能會形成對象的引用關係發生變化。
  4. 併發清除:這是最後一個階段,也是和用戶線程同時運行的。將併發的清理掉被標記的死亡對象。

其中初始標記和從新標記仍然會STW暫停用戶線程,可是這兩個過程的停頓時間相對於併發標記和併發清除而言相對較短,而併發標記和併發清除階段GC線程則能夠和用戶線程併發運行。

因爲CMS收集器一樣使用標記清除算法,因此存在內存碎片問題,從而可能形成大對象沒法分配發生提早GC。因此CMS收集器又提供了參數控制其進行內存碎片整理,默認是開啓狀態,這個過程是很是長的。

三.GC匹配和參數使用

從以上內容介紹,能夠看出分代GC分爲不少種,隨着演化過程,每種都有各自的應用場景。從其收集特色上能夠分爲三類:

  1. 單線程串行收集
  2. 多線程併發串行收集
  3. 多階段並行收集

雖然這些分代收集器種類繁多,可是他們之間有相互匹配,並不是任意使用。配對的使用狀況以及參數見下表:

young old 參數
Serial Serial old -XX:+UseSerialGC
ParNew Serial old -XX:+UseParNewGC
Parallel Scavenge Serial old -XX:+UseParallelGC
Parallel Scavenge Parallel Old -XX:+UseParallelOldGC
ParNew CMS + Serial Old -XX:+UseConcMarkSweepGC
Serial CMS -XX:+UseConcMarkSweepGC -XX:-UseParNewGC


四.GC日誌格式

GC收集器衆多,每種GC收集器的GC日誌格式都不同。這裏筆者作了下總結,一樣是針對以上的各類匹配狀況作了日誌格式的收集。

1.Serial + Serial old格式

0.299: [GC (Allocation Failure) 0.299: [DefNew: 1770K->428K(4928K), 0.0019955 secs] 9962K->8620K(15872K), 0.0020405 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.304: [GC (Allocation Failure) 0.304: [DefNew (promotion failed) : 4612K->4193K(4928K), 0.0014249 secs]0.305: [Tenured: 8588K->4499K(10944K), 0.0034912 secs] 12804K->4499K(15872K), [Metaspace: 3094K->3094K(1056768K)], 0.0049774 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

使用Serial + Serial Old搭配式,年輕代收集將會是DefNew,老年代將會是Tenured。

1. ParNew + Serial old格式

0.243: [GC (Allocation Failure) 0.243: [ParNew: 1770K->470K(4928K), 0.0029402 secs] 9962K->8662K(15872K), 0.0029933 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
0.251: [GC (Allocation Failure) 0.251: [ParNew (promotion failed): 4566K->4096K(4928K), 0.0009985 secs]0.252: [Tenured: 8632K->4516K(10944K), 0.0025514 secs] 12758K->4516K(15872K), [Metaspace: 3093K->3093K(1056768K)], 0.0035906 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

使用Serial + Serial Old搭配式,年輕代收集將會是ParNew,老年代將會是Tenured。

3.Parallel Scavenge + Parallel Old格式

0.224: [GC (Allocation Failure) [PSYoungGen: 0K->0K(4608K)] 8619K->8619K(15872K), 0.0004876 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.225: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(4608K)] [ParOldGen: 8619K->8601K(11264K)] 8619K->8601K(15872K), [Metaspace: 3080K->3080K(1056768K)], 0.0046498 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]

使用Parallel Scavenge + Parallel Old搭配式,年輕代收集將會是PSYoungGen,老年代將會是ParOldGen。

4.ParNew + CMS格式

0.351: [GC (Allocation Failure) 0.351: [ParNew: 1769K->446K(4928K), 0.0008713 secs] 9961K->8638K(15872K), 0.0009216 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
0.354: [GC (CMS Initial Mark) [1 CMS-initial-mark: 8192K(10944K)] 12734K(15872K), 0.0004513 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.355: [CMS-concurrent-mark-start]
0.355: [CMS-concurrent-mark: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.355: [GC (Allocation Failure) 0.355: [ParNew (promotion failed): 4631K->4267K(4928K), 0.0007242 secs]0.356: [CMS (concurrent mode failure): 8207K->4500K(10944K), 0.0031171 secs] 12823K->4500K(15872K), [Metaspace: 3080K->3080K(1056768K)], 0.0038915 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

使用CMS收集器的GC日誌格式很是明顯,有CMS的GC過程,年輕代將使用ParNew標誌。


五.經常使用的GC參數總結

1.開啓GC日誌

  • -verbose:gc:打印GC日誌
  • -XX:+PrintGCDateStamps:打印GC日誌時間戳
  • -XX:PrintGCDetails:打印GC日誌詳情
  • -XX:+PrintGCTimeStamps:打印這次GC距離JVM開始運行的時間
  • -XX:+PrintGCApplicationStopedTime:打印GC形成的應用暫停時間
  • -XX:+PrintTenuringDistribution:打印對象晉升日誌

2.通用參數

  • -XX:+HeapDumpOnOutOfMemoryError:內存溢出時,產生heap dump文件
  • -Xloggc::將GC日誌輸出到指定文件
  • -XX:-+DisableExplicitGC:禁用System.gc(),該方法默認會觸發FGC
  • -XX:MaxTenuringThreshold: 新生代 to 區的對象在通過屢次 GC 後,若是尚未死亡,則認爲他是一個老對象,則能夠晉升到老年代,默認是15。但該參數不是惟一決定對象晉升的條件,當 to區不夠或者該對象年齡已經達到了平均晉升值或者大對象等等條件
  • -XX:TargetSurvivorRatio 決定對什麼時候晉升的不只只有 XX:MaxTenuringThreshold 參數,若是在Survivor空間中相同年齡全部對象大小的總和大於Survivor空間的一半(默認50%)
  • -XX:+UseTLAB:啓用線程本地分配緩存,默認開啓
  • -XX:+PrintTLAB:打印TLAB的使用狀況
參考

Our Collectors
ParNew 和 PSYoungGen 和 DefNew 是一個東西麼?

相關文章
相關標籤/搜索