天天看點

《深入了解Java虛拟機》讀書筆記:垃圾收集器

作者:知北遊zzz
《深入了解Java虛拟機》讀書筆記:垃圾收集器

垃圾收集器

HotSpot虛拟機包含的所有收集器如圖3-5所示。圖3-5展示了7種作用于不同分代的收集器,如果兩個收集器之間存在連線,就說明它們可以搭配使用。

新生代收集器:Serial、ParNew、Parallel Scavenge,新生代收集器均采用複制算法

老年代收集器:Serial Old(标記-整理算法)、Parallel Old(标記-整理算法)、CMS(标記-清除算法)

不分代的收集器:G1(整體來看基于标記-整理和局部來看基于複制算法)

《深入了解Java虛拟機》讀書筆記:垃圾收集器

圖3-5 HotSpot虛拟機的垃圾收集器

一、Serial收集器

Serial收集器是一個單線程的收集器,它的“單線程”的意義并不僅僅說明它隻會使用一個CPU或一條收集線程去完成垃圾收集工作,更重要的是在它進行垃圾收集時,必須暫停其他所有的工作線程,直到它收集結束。這個過程常被稱為“Stop The World”。

實際上到現在為止,它依然是虛拟機運作在Client模式下的預設新生代收集器。它也有着優于其他收集器的地方:簡單而高效(與其他收集器的單線程比),對于限定單個CPU的環境來說,Serial收集器由于沒有線程互動的開銷,專心做垃圾收集自然可以獲得最高的單線程收集效率。

《深入了解Java虛拟機》讀書筆記:垃圾收集器

圖3-6 Serial/Serial Old收集器運作示意圖

二、 ParNew收集器

ParNew收集器其實就是Serial收集器的多線程版本,除了使用多條線程進行垃圾收集之外,其餘行為包括Serial收集器可用的所有控制參數、收集算法、Stop The World、對象配置設定規則、回收政策等都與Serial收集器完全一樣,在實作上,這兩種收集器也共用了相當多的代碼。ParNew收集器的工作過程如圖3-7所示。

它是許多運作在Server模式下的虛拟機中首選的新生代收集器,其中有一個與性能無關但很重要的原因是,除了Serial收集器外,目前隻有它能與CMS收集器配合工作。

《深入了解Java虛拟機》讀書筆記:垃圾收集器

圖3-7 ParNew/Serial Old收集器運作示意圖

三、Parallel Scavenge收集器

Parallel Scavenge收集器是一個新生代收集器,它也是使用複制算法的收集器,又是并行的多線程收集器……看上去和ParNew都一樣,那它有什麼特别之處呢?Parallel Scavenge收集器的特點是它的關注點與其他收集器不同,CMS等收集器的關注點是盡可能地縮短垃圾收集時使用者線程的停頓時間,而Parallel Scavenge收集器的目标則是達到一個可控制的吞吐量(Throughput)。所謂吞吐量就是CPU用于運作使用者代碼的時間與CPU總消耗時間的比值,即吞吐量 = 運作使用者代碼時間 /(運作使用者代碼時間 +垃圾收集時間),虛拟機總共運作了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。

四 、Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同樣是一個單線程收集器,使用“标記-整理”算法。這個收集器的主要意義也是在于給Client模式下的虛拟機使用。如果在Server模式下,那麼它主要還有兩大用途:一種用途是在JDK 1.5以及之前的版本中與Parallel Scavenge收集器搭配使,另一種用途就是作為CMS收集器的後備預案,在并發收集發生Concurrent Mode Failure時使用。這兩點都将在後面的内容中詳細講解。Serial Old收集器的工作過程如圖3-8所示。

《深入了解Java虛拟機》讀書筆記:垃圾收集器

圖3-8 Serial/Serial Old收集器運作示意圖

五、Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“标記-整理”算法。直到Parallel Old收集器出現後,“吞吐量優先”收集器終于有了比較名副其實的應用組合,在注重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel Scavenge加Parallel Old收集器。Parallel Old收集器的工作過程如圖3-9所示。

《深入了解Java虛拟機》讀書筆記:垃圾收集器

圖3-9 Parallel Scavenge/Parallel Old收集器運作示意圖

六、CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以擷取最短回收停頓時間為目标的收集器。它在垃圾收集時使得使用者線程和GC線程并發執行,是以在GC過程中使用者也不會感受到明顯示卡頓.但使用者線程和GC線程之間不停地切換會有額外的開銷,是以垃圾回收總時間就會被延長。CMS收集器是基于“标記—清除”算法實作的。

1、運作過程

它的運作過程相對于前面幾種收集器來說更複雜一些,整個過程分為4個步驟,包括:

(1)初始标記(CMS initial mark)

(2)并發标記(CMS concurrent mark)

(3)重新标記(CMS remark)

(4)并發清除(CMS concurrent sweep)

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

由于整個過程中耗時最長的并發标記和并發清除過程收集器線程都可以與使用者線程一起工作,是以,從總體上來說,CMS收集器的記憶體回收過程是與使用者線程一起并發執行的。通過圖3-10可以比較清楚地看到CMS收集器的運作步驟中并發和需要停頓的時間。

《深入了解Java虛拟機》讀書筆記:垃圾收集器

圖3-10 Concurrent Mark Sweep收集器運作示意圖

2、CMS的缺點

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

(1)CMS收集器對CPU資源非常敏感。

當CPU不足4個(譬如2個)時,CMS對使用者程式的影響就可能變得很大,如果本來CPU負載就比較大,還分出一半的運算能力去執行收集器線程,就可能導緻使用者程式的執行速度忽然降低了50%,其實也讓人無法接受。

(2)CMS收集器無法處理浮動垃圾

由于垃圾清除過程中,使用者線程和GC線程并發執行,也就是使用者線程仍在執行,那麼在執行過程中會産生垃圾,這些垃圾稱為"浮動垃圾".

(3)會産生大量碎片空間

CMS是一款基于“标記—清除”算法實作的收集器,收集結束時會有大量空間碎片産生。

(4)吞吐量低

由于CMS在垃圾收集過程使用使用者線程和GC線程并行執行,進而線程切換會有額外開銷,是以CPU吞吐量就不如在GC過程中停止一切使用者線程的方式來的高。

七、G1收集器

1、G1收集器概述

G1(Garbage-First)收集器,追求停頓時間、多線程GC、面向服務端應用。整體來看基于标記-整理和局部來看基于複制算法合并。

它将整個Java堆劃分為多個大小相等的獨立區域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是實體隔離的了,它們都是一部分Region(不需要連續)的集合。

G1跟蹤各個Region裡面的垃圾堆積的價值大小(回收所獲得的空間大小以及回收所需時間的經驗值),在背景維護一個優先清單,每次根據允許的收集時間,優先回收價值最大的Region(這也就是Garbage-First名稱的來由)。

2、G1的特點。

(1)并行與并發

G1能充分利用多CPU、多核環境下的硬體優勢,使用多個CPU(CPU或者CPU核心)來縮短Stop-The-World停頓的時間,部分其他收集器原本需要停頓Java線程執行的GC動作,G1收集器仍然可以通過并發的方式讓Java程式繼續執行。

(2)分代收集

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

(3)空間整合

與CMS的“标記—清理”算法不同,G1從整體來看是基于“标記—整理”算法實作的收集器,從局部(兩個Region之間)上來看是基于“複制”算法實作的,但無論如何,這兩種算法都意味着G1運作期間不會産生記憶體空間碎片,收集後能提供規整的可用記憶體。這種特性有利于程式長時間運作,配置設定大對象時不會因為無法找到連續記憶體空間而提前觸發下一次GC。

(4)可預測的停頓

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

3、G1收集器的運作過程

G1收集器的運作大緻可劃分為以下幾個步驟:

(1)初始标記(Initial Marking)

(2)并發标記(Concurrent Marking)

(3)最終标記(Final Marking)

(4)篩選回收(Live Data Counting and Evacuation)

繼續閱讀