前兩篇《JVM入門——運行時數據區》《JVM常見垃圾回收演算法》所提到的實際上JVM規範以及常用的垃圾回收演算法,具體的JVM實現實際上不止一種,有JRockit、J9等待,當然最有名當屬HotSpot JVM。下麵是HotSpot JVM的整體架構圖,本文著重介紹HotSpot中的垃圾回收器(Garb ...
前兩篇《JVM入門——運行時數據區》《JVM常見垃圾回收演算法》所提到的實際上JVM規範以及常用的垃圾回收演算法,具體的JVM實現實際上不止一種,有JRockit、J9等待,當然最有名當屬HotSpot JVM。下麵是HotSpot JVM的整體架構圖,本文著重介紹HotSpot中的垃圾回收器(Garbage Collector)。
現有的HotSpot垃圾回收器以及之間的關係和應用範圍如下圖所示:
其中G1 GC非常顯眼的處於新生代和老年代之間,可以猜測這個G1 GC可同時運用在新生代和老年代,確實可以說G1是一個劃時代新概念GC。
在介紹上面的垃圾回收器之前要先說明JVM虛擬機的Client模式和Server模式,Java所能做的事一是做客戶端簡單說就是GUI桌面應用程式,二是可以用作伺服器端。兩種模式Client模式啟動快,啟動後性能較差,Server模式啟動慢,啟動後性能較高。
Serial GC(-XX:+UseSerialGC,複製演算法,新生代)
這是一個比較古老的垃圾收集器,我理解它為“簡單粗暴”,簡單粗暴的方法往往可以應對簡單的環境,事實上Serial GC在Client模式下正是如此。它是一個串列的垃圾收集器,串列意味著就算是有多核處理器也不會有多個線程來並行回收,在串列的同時,其它的正常工作線程也要停止工作,稱為“Stop the world”。這實際很好理解,你在清掃垃圾的時候,總不希望有人同時在丟垃圾吧。當然Serial GC在如今的HotSpot JVM中Server模式下已經幾乎廢棄。另外,它工作使用垃圾回收的“複製演算法”工作在Java堆的新生代。
ParNew GC(-XX:+UseParNewGC,複製演算法,新生代)
ParNew GC實際上是Serial GC的多線程版本。上面提到了Serial GC即使是多核CPU的環境下也是單線程進行垃圾記憶體的回收。此垃圾收集器側可以做到多線程環境下進行垃圾記憶體的回收,這個多線程也僅僅是垃圾回收的多線程,而不是與用戶線程併發執行。並且只有它能與CMS老年代的垃圾回收器配合使用,而CMS又恰恰是劃時代意義的垃圾回收器,所以當JVM的老年代垃圾回收器是CMS的話,新生代的垃圾回收器通常是ParNew GC。
Parallel GC(-XX:+UseParallelGC,複製演算法,新生代)
它有點和ParaNew GC類似,從名字上來看也是並行的多線程收集器。我們之前提到過,在進行GC的過程中要“Stop the world”,停頓時間越短當然越好,很多垃圾回收器(包括前兩個)關註的就是如何提高停頓時間。而Parallel GC關註的則是吞吐量。它關註的是垃圾回收的整體耗時,如果垃圾回收所占用的整體耗時較短,則吞吐量高,CPU就能將越多的時間用於任務的執行上,(吞吐量 = 任務運行時間 / (任務運行時間 + 垃圾回收時間))。
Serial Old GC(-XX:+UseSerialOldGC,標記-壓縮演算法,老年代)
它是Serial GC的老年代版本,同樣也是單線程,也能與Parallel GC配合使用作為它的老年代GC。
Parallel Old GC(-XX:+UseParallelOldGC,標記-壓縮演算法,老年代)
為了避免如果在新生代選擇了Parallel GC而老年代則只有選擇Serial Old GC的困境,出現了Parallel GC的老年代版本——Parallel Old GC。故如果在一些需要高吞吐量的常量利用Parallel GC和Parallel Old GC組合將會是一個很好的選擇。
☆Concurrent Mark Sweep(CMS) GC (-XX:+UseConcMarkSweepGC,標記-清除演算法,老年代)
CMS GC幾乎占據著JVM老年代垃圾收集器的半壁江山,它劃時代的意義就是垃圾回收線程幾乎能用戶線程做到同時工作。“幾乎”是因為它還是不能做到完全不需要“Stop the world”,只是它儘可能的縮短了停頓時間。
它的整個垃圾回收過程可分為以下4個步驟:
- 初始標記
- 併發標記
- 重新標記
- 併發清理
這4個步驟“初始標記”和“重新標記”需要進行短暫的“Stop the world”,併發標記的過程實際上就是和用戶線程同時工作,也就是“一邊丟垃圾,一邊打掃”,這樣就會帶來一個問題,如果垃圾的產生是在標記後發生,那麼這次垃圾就只有等到下次再回收。當然等到標記完了過後垃圾自然不會和用戶線程產生衝突,而清理過程就能和用戶線程同時處理了。對於此垃圾回收器有一個比較顯著且不可避免的一個問題就是它所採用的是“標記-清除”演算法,也就是說它不同會壓縮存活的對象,這樣就會帶來記憶體空間碎片化的問題,如果出現需要分配一個連續的較大的記憶體空間則只能觸發一次Full GC。上一篇《JVM常見垃圾回收演算法》中談到了在新生代的垃圾回收稱為“Minor GC”,老年代的垃圾回收稱為“Major GC”,而“Full GC”則是在整個堆上觸發一次垃圾回收,可想而知代價會相當高,而且此時不得不暫停用戶線程,只能針對具體使用場景通過調整CMS GC的參數對其進行調整優化。
☆Garbage-First(G1) GC(-XX:+UseG1GC)
G1 GC較之前所有的垃圾回收器都不同,從開頭的第二幅圖就能看出,它涵蓋了新生代和老年代,或者說僅僅是從邏輯上還保留“新生代”和“老年代”這種說法,實際上它已不存在記憶體分代,它在JDK6中僅僅是實驗版,在JDK7u4過後才正式商用,對於此垃圾回收器我將會單獨對其講解,另外它的論文地址在:http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.63.6386&rep=rep1&type=pdf。