垃圾收集器 HotSpot虛擬機包含的所有收集器如圖3-5所示。圖3-5展示了7種作用於不同分代的收集器,如果兩個收集器之間存在連線,就說明它們可以搭配使用。 新生代收集器:Serial、ParNew、Parallel Scavenge,新生代收集器均採用複製演算法 老年代收集器:Serial Old ...
HotSpot虛擬機包含的所有收集器如圖3-5所示。圖3-5展示了7種作用於不同分代的收集器,如果兩個收集器之間存在連線,就說明它們可以搭配使用。
新生代收集器:Serial、ParNew、Parallel Scavenge,新生代收集器均採用複製演算法
老年代收集器:Serial Old(標記-整理演算法)、Parallel Old(標記-整理演算法)、CMS(標記-清除演算法)
不分代的收集器:G1(整體來看基於標記-整理和局部來看基於複製演算法)
一、Serial收集器
Serial收集器是一個單線程的收集器,它的“單線程”的意義並不僅僅說明它只會使用一個CPU或一條收集線程去完成垃圾收集工作,更重要的是在它進行垃圾收集時,必須暫停其他所有的工作線程,直到它收集結束。這個過程常被稱為“Stop The World”。
實際上到現在為止,它依然是虛擬機運行在Client模式下的預設新生代收集器。它也有著優於其他收集器的地方:簡單而高效(與其他收集器的單線程比),對於限定單個CPU的環境來說,Serial收集器由於沒有線程交互的開銷,專心做垃圾收集自然可以獲得最高的單線程收集效率。
二、 ParNew收集器
ParNew收集器其實就是Serial收集器的多線程版本,除了使用多條線程進行垃圾收集之外,其餘行為包括Serial收集器可用的所有控制參數、收集演算法、Stop The World、對象分配規則、回收策略等都與Serial收集器完全一樣,在實現上,這兩種收集器也共用了相當多的代碼。ParNew收集器的工作過程如圖3-7所示。
它是許多運行在Server模式下的虛擬機中首選的新生代收集器,其中有一個與性能無關但很重要的原因是,除了Serial收集器外,目前只有它能與CMS收集器配合工作。
三、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所示。
五、Parallel Old收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標記-整理”演算法。直到Parallel Old收集器出現後,“吞吐量優先”收集器終於有了比較名副其實的應用組合,在註重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel Scavenge加Parallel Old收集器。Parallel Old收集器的工作過程如圖3-9所示。
六、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收集器的運作步驟中併發和需要停頓的時間。
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)