HotSpot垃圾收集器(截止到Java 12)

目錄

1 Serial / Serial Old收集器

2 ParNew收集器

3 Parallel Scavenge / Parallel Old收集器

4 CMS收集器

5 G1收集器

6 ZGC收集器

7 Epsilon收集器

8 Shenandoah收集器


如果說垃圾收集算法是內存回收的方法論,垃圾收集器就是內存回收的具體實現(以下垃圾收集器介紹僅限於HotSpot虛擬機


1 Serial / Serial Old收集器

串行收集器是最古老、最穩定以及效率高的收集器,可能會產生較長的停頓,只使用一個線程去回收。新生代、老年代使用串行回收;垃圾收集的過程中會“Stop The World”(服務暫停)。

Serial Old是Serial收集器的老年代版本,它同樣是一個單線程收集器,使用標記-整理算法。


2 ParNew收集器

ParNew收集器其實就是Serial收集器的多線程版本。除了使用多線程進行垃圾收集之外,其餘行爲包括Serial收集器可用的所有控制參數、收集算法、“Stop The World”、對象分配規則、回收策略等都與Serial收集器完全一樣。


3 Parallel Scavenge / Parallel Old收集器

Parallel Scavenge收集器是一個新生代收集器,它的關注點與其他收集器不同,CMS等收集器的關注點是儘可能地縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標則是達到一個可控制的吞吐量。所謂吞吐量就是CPU用於運行用戶代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間),虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。

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


4 CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。從名字上就可以看出CMS收集器是基於標記-清除算法實現的,收集的範圍是老年代。整個過程分爲4個步驟:包括:

  • 初始標記(CMS initial mark)
  • 併發標記(CMS concurrent mark)
  • 重新標記(CMS remark)
  • 併發清除(CMS concurrent sweep)

其中初始標記、重新標記這兩個步驟仍然需要“Stop The World”。初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,併發標記階段就是進行GC Root Tracing的過程,而重新標記階段則是爲了修正併發標記期間,因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短。

由於整個過程中耗時最長的併發標記和併發清除過程中,收集器線程都可以與用戶線程一起工作,所以總體上來說,CMS收集器的內存回收過程是與用戶線程一起併發地執行。

優點:併發收集、低停頓

缺點:產生大量空間碎片、併發階段會降低吞吐量(可以選擇開啓碎片整理功能)


5 G1收集器

上面提到的垃圾收集器,收集的範圍都是整個新生代或者老年代,而G1不再是這樣。使用G1收集器時,Java堆的內存佈局與其他收集器有很大差別,它將整個Java堆劃分爲多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔閡了,它們都是一部分(可以不連續)Region的集合。

G1收集器的運作過程與CMS較爲相似,其過程如下:

  • 初始標記(Initial Marking):僅標記一下GC Roots能直接關聯到的對象,且修改TAMS(Next Top at Mark Start),讓下一階段併發運行時,用戶程序能在正確可用的Region中創建新對象。需要“Stop The World”,但速度很快。
  • 併發標記(Concurrent Marking):進行GC Root Tracing的過程,上一步產生的集合中標記出存活對象。耗時較長,但應用程序也在運行。並不能保證可以標記出所有的存活對象。
  • 最終標記(Final Marking):爲了修正併發標記期間,因用戶程序繼續運作而導致標記變動的那一部分對象的標記記錄。需要“Stop The World”,且停頓時間比初始標記稍長,但遠比並發標記短。採用多線程並行執行來提升效率。
  • 篩選回收(Live Data Counting and Evacuation):首先排序各個Region的回收價值和成本,然後根據用戶期望的GC停頓時間來制定回收計劃。最後按計劃回收一些價值高的Region中的垃圾對象。從局部上來看,回收時採用複製算法,從一個或多個Region中複製存活對象到堆上的另一個空的Region,並且在此過程中壓縮和釋放內存。可以併發進行,降低停頓時間,並增加吞吐量。

不同於CMS使用的標記-清除算法,G1從整體來看是基於標記-整理算法實現的收集器,從局部上來看是基於“複製”算法實現的。將一個region中的存活對象複製到另一個region中,這種不會像CMS那樣回收完因爲有很多內存碎片還需要整理一次,G1採用複製算法回收幾乎不會有太多內存碎片。

G1相對於CMS另外一個更大的優勢,就是可以設置可預測的停頓模型,能夠使開發者明確指定在長度爲M毫秒的時間片段內,消耗在垃圾收集器上的時間不能超過N毫秒。

需要說明的一點是,從Java 9開始,默認的垃圾收集器已經改爲了G1,代替了之前的Parallel Scavenge(新生代) + Parallel  Old(老年代)的組合。


6 ZGC收集器

ZGC收集器是從Java 11開始支持的實驗性垃圾收集器,估計等到正式商用還要有個幾年的時間。其目標如下:

  • GC暫停時間不應超過10毫秒
  • 處理堆的大小從相對較小(幾百兆字節)到非常大(多太字節)不等
  • 與使用G1相比,應用程序吞吐量減少不超過15%
  • 爲未來的GC功能和優化利用彩色指針和負載障礙奠定基礎
  • 最初支持的平臺:Linux / x64

7 Epsilon收集器

Java 11還加入了一個比較特殊的垃圾收集器——Epsilon,該垃圾收集器被稱爲“no-op”收集器,將處理內存分配而不實施任何實際的內存回收機制。 也就是說,這是一款不做垃圾回收的垃圾回收器。這個垃圾回收器看起來並沒什麼用,主要可以用來進行性能測試、內存壓力測試等,Epsilon GC可以作爲度量其他垃圾回收器性能的對照組。大神Martijn說,Epsilon GC至少能夠幫助理解GC的接口,有助於成就一個更加模塊化的JVM。


8 Shenandoah收集器

Java 12中新加入的垃圾收集器,Shenandoah最初的目標是把GC停頓時間降到10毫秒以下,並且對內存的支持擴展到TB級別。爲了降低停頓時間,回收器需要使用更多的線程來並行處理回收任務。而要在降低停頓時間的同時能夠支持更大的堆空間,回收器對CPU的多核處理能力提出了更高的要求。相比於CMS和G1,Shenandoah不僅進行並行的垃圾標記,在壓縮堆空間時也是並行進行的。

值得一提的是,作爲首個由非Oracle開發,由RedHat領導開發的垃圾收集器,其目標又與Oracle在Java 11中發佈的ZGC幾乎完全一致,兩者天生就存在競爭。於是Oracle馬上用實際行動抵制了這個新收集器,在OracleJDK 12裏把Shenandoah的代碼通過條件編譯強行剔除掉(OpenJDK 12中仍然可用)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章