《深入理解java虛擬機器》學習筆記4——Java虛擬機器垃圾收集器

yangxi_001發表於2013-12-04

Java堆記憶體被劃分為新生代和年老代兩部分,新生代主要使用複製和標記-清除垃圾回收演算法,年老代主要使用標記-整理垃圾回收演算法,因此java虛擬中針對新生代和年老代分別提供了多種不同的垃圾收集器,JDK1.6中Sun HotSpot虛擬機器的垃圾收集器如下:

圖中如果兩個垃圾收集器直接有連線,則表明這兩個垃圾收集器可以搭配使用。

(1).Serial垃圾收集器:

Serial是最基本、歷史最悠久的垃圾收集器,使用複製演算法,曾經是JDK1.3.1之前新生代唯一的垃圾收集器。

Serial是一個單執行緒的收集器,它不僅僅只會使用一個CPU或一條執行緒去完成垃圾收集工作,並且在進行垃圾收集的同時,必須暫停其他所有的工作執行緒,直到垃圾收集結束。

Serial垃圾收集器雖然在收集垃圾過程中需要暫停所有其他的工作執行緒,但是它簡單高效,對於限定單個CPU環境來說,沒有執行緒互動的開銷,可以獲得最高的單執行緒垃圾收集效率,因此Serial垃圾收集器依然是java虛擬機器執行在Client模式下預設的新生代垃圾收集器。

(2).ParNew垃圾收集器:

ParNew垃圾收集器其實是Serial收集器的多執行緒版本,也使用複製演算法,除了使用多執行緒進行垃圾收集之外,其餘的行為和Serial收集器完全一樣,ParNew垃圾收集器在垃圾收集過程中同樣也要暫停所有其他的工作執行緒。

ParNew收集器預設開啟和CPU數目相同的執行緒數,可以通過-XX:ParallelGCThreads引數來限制垃圾收集器的執行緒數。

ParNew雖然是除了多執行緒外和Serial收集器幾乎完全一樣,但是ParNew垃圾收集器是很多java虛擬機器執行在Server模式下新生代的預設垃圾收集器。

(3).Parallel Scavenge收集器:

Parallel Scavenge收集器也是一個新生代垃圾收集器,同樣使用複製演算法,也是一個多執行緒的垃圾收集器,它重點關注的是程式達到一個可控制的吞吐量(Thoughput,CPU用於執行使用者程式碼的時間/CPU總消耗時間,即吞吐量=執行使用者程式碼時間/(執行使用者程式碼時間+垃圾收集時間)),高吞吐量可以最高效率地利用CPU時間,儘快地完成程式的運算任務,主要適用於在後臺運算而不需要太多互動的任務。

Parallel Scavenge收集器提供了兩個引數用於精準控制吞吐量:

a.-XX:MaxGCPauseMillis:控制最大垃圾收集停頓時間,是一個大於0的毫秒數。

b.-XX:GCTimeRation:直接設定吞吐量大小,是一個大於0小於100的整數,也就是程式執行時間佔總時間的比率,預設值是99,即垃圾收集執行最大1%(1/(1+99))的垃圾收集時間。

Parallel Scavenge是吞吐量優先的垃圾收集器,它還提供一個引數:-XX:+UseAdaptiveSizePolicy,這是個開關引數,開啟之後就不需要手動指定新生代大小(-Xmn)、Eden與Survivor區的比例(-XX:SurvivorRation)、新生代晉升年老代物件年齡(-XX:PretenureSizeThreshold)等細節引數,虛擬機器會根據當前系統執行情況收集效能監控資訊,動態調整這些引數以達到最大吞吐量,這種方式稱為GC自適應調節策略,自適應調節策略也是ParallelScavenge收集器與ParNew收集器的一個重要區別。

(4).Serial Old收集器:

Serial Old是Serial垃圾收集器年老代版本,它同樣是個單執行緒的收集器,使用標記-整理演算法,這個收集器也主要是執行在Client預設的java虛擬機器預設的年老代垃圾收集器。

在Server模式下,主要有兩個用途:

a.在JDK1.5之前版本中與新生代的Parallel Scavenge收集器搭配使用。

b.作為年老代中使用CMS收集器的後備垃圾收集方案。

新生代Serial與年老代Serial Old搭配垃圾收集過程圖:


新生代Parallel Scavenge收集器與ParNew收集器工作原理類似,都是多執行緒的收集器,都使用的是複製演算法,在垃圾收集過程中都需要暫停所有的工作執行緒。

新生代Parallel Scavenge/ParNew與年老代Serial Old搭配垃圾收集過程圖:


(5).Parallel Old收集器:

Parallel Old收集器是Parallel Scavenge的年老代版本,使用多執行緒的標記-整理演算法,在JDK1.6才開始提供。

在JDK1.6之前,新生代使用ParallelScavenge收集器只能搭配年老代的Serial Old收集器,只能保證新生代的吞吐量優先,無法保證整體的吞吐量,Parallel Old正是為了在年老代同樣提供吞吐量優先的垃圾收集器,如果系統對吞吐量要求比較高,可以優先考慮新生代Parallel Scavenge和年老代Parallel Old收集器的搭配策略。

新生代Parallel Scavenge和年老代Parallel Old收集器搭配執行過程圖:


(6).CMS收集器:

Concurrent mark sweep(CMS)收集器是一種年老代垃圾收集器,其最主要目標是獲取最短垃圾回收停頓時間,和其他年老代使用標記-整理演算法不同,它使用多執行緒的標記-清除演算法。

最短的垃圾收集停頓時間可以為互動比較高的程式提高使用者體驗,CMS收集器是Sun HotSpot虛擬機器中第一款真正意義上併發垃圾收集器,它第一次實現了讓垃圾收集執行緒和使用者執行緒同時工作。

CMS工作機制相比其他的垃圾收集器來說更復雜,整個過程分為以下4個階段:

a.初始標記:只是標記一下GC Roots能直接關聯的物件,速度很快,仍然需要暫停所有的工作執行緒。

b.併發標記:進行GC Roots跟蹤的過程,和使用者執行緒一起工作,不需要暫停工作執行緒。

c.重新標記:為了修正在併發標記期間,因使用者程式繼續執行而導致標記產生變動的那一部分物件的標記記錄,仍然需要暫停所有的工作執行緒。

d.併發清除:清除GC Roots不可達物件,和使用者執行緒一起工作,不需要暫停工作執行緒。

由於耗時最長的併發標記和併發清除過程中,垃圾收集執行緒可以和使用者現在一起併發工作,所以總體上來看CMS收集器的記憶體回收和使用者執行緒是一起併發地執行。

CMS收集器工作過程:


CMS收集器有以下三個不足:

a.CMS收集器對CPU資源非常敏感,其預設啟動的收集執行緒數=(CPU數量+3)/4,在使用者程式本來CPU負荷已經比較高的情況下,如果還要分出CPU資源用來執行垃圾收集器執行緒,會使得CPU負載加重。

b.CMS無法處理浮動垃圾(Floating Garbage),可能會導致Concurrent ModeFailure失敗而導致另一次Full GC。由於CMS收集器和使用者執行緒併發執行,因此在收集過程中不斷有新的垃圾產生,這些垃圾出現在標記過程之後,CMS無法在本次收集中處理掉它們,只好等待下一次GC時再將其清理掉,這些垃圾就稱為浮動垃圾。

CMS垃圾收集器不能像其他垃圾收集器那樣等待年老代機會完全被填滿之後再進行收集,需要預留一部分空間供併發收集時的使用,可以通過引數-XX:CMSInitiatingOccupancyFraction來設定年老代空間達到多少的百分比時觸發CMS進行垃圾收集,預設是68%。

如果在CMS執行期間,預留的記憶體無法滿足程式需要,就會出現一次ConcurrentMode Failure失敗,此時虛擬機器將啟動預備方案,使用Serial Old收集器重新進行年老代垃圾回收。

c.CMS收集器是基於標記-清除演算法,因此不可避免會產生大量不連續的記憶體碎片,如果無法找到一塊足夠大的連續記憶體存放物件時,將會觸發因此Full GC。CMS提供一個開關引數-XX:+UseCMSCompactAtFullCollection,用於指定在Full GC之後進行記憶體整理,記憶體整理會使得垃圾收集停頓時間變長,CMS提供了另外一個引數-XX:CMSFullGCsBeforeCompaction,用於設定在執行多少次不壓縮的Full GC之後,跟著再來一次記憶體整理。

(7).G1收集器:

Garbage first垃圾收集器是目前垃圾收集器理論發展的最前沿成果,相比與CMS收集器,G1收集器兩個最突出的改進是:

a.基於標記-整理演算法,不產生記憶體碎片。

b.可以非常精確控制停頓時間,在不犧牲吞吐量前提下,實現低停頓垃圾回收。

G1收集器避免全區域垃圾收集,它把堆記憶體劃分為大小固定的幾個獨立區域,並且跟蹤這些區域的垃圾收集進度,同時在後臺維護一個優先順序列表,每次根據所允許的收集時間,優先回收垃圾最多的區域。

區域劃分和優先順序區域回收機制,確保G1收集器可以在有限時間獲得最高的垃圾收集效率。

Java虛擬機器常用的垃圾收集器相關引數如下:

引數

描述

UseSerialGC

虛擬機器執行在Client模式的預設值,開啟此開關引數後,
使用Serial+Serial Old收集器組合進行垃圾收集。

UseParNewGC

開啟此開關引數後,使用ParNew+Serial Old收集器組合進
行垃圾收集。

UseConcMarkSweepGC

開啟此開關引數後,使用ParNew+CMS+Serial Old收集器組
合進行垃圾收集。Serial Old作為CMS收集器出現Concurrent 
Mode Failure的備用垃圾收集器。

UseParallelGC

虛擬機器執行在Server模式的預設值,開啟此開關引數後,
使用Parallel Scavenge+Serial Old收集器組合進行垃圾收集。

UseParallelOldGC

開啟此開關引數後,
使用Parallel Scavenge+Parallel Old收集器組合進行垃圾收集。

SurvivorRation

新生代記憶體中Eden區域與Survivor區域容量比值,預設是8,即
Eden:Survivor=8:1.

PretenureSizeThreshold

直接晉升到年老代的物件大小,設定此引數後,超過該大小的
物件直接在年老代中分配記憶體。

MaxTenuringThreshold

直接晉升到年老代的物件年齡,每個物件在一次Minor GC之後還
存活,則年齡加1,當年齡超過該值時進入年老代。

UseAdaptiveSizePolicy

java虛擬機器動態自適應策略,動態調整年老代物件年齡和各個區域大小。

HandlePromotionFailure

是否允許擔保分配記憶體失敗,即整個年老代空間不足,而整個新生代中Eden和Survivor物件都存活的極端情況。

ParallelGCThreads

設定並行GC時進行記憶體回收的執行緒數。

GCTimeRation

Parallel Scavenge收集器執行時間佔總時間比率。

MaxGCPauseMillis

Parallel Scavenge收集器最大GC停頓時間。

CMSInitiatingOccupancyFraction

設定CMS收集器在年老代空間被使用多少百分比之後觸發垃圾收集,預設是68%。

UseCMSCompactAtFullCollection

設定CMS收集器在完成垃圾收集之後是否進行一次記憶體整理。

CMSFullGCsBeforeCompaction

設定CMS收集器在進行多少次垃圾收集之後才進行一次記憶體整理。

java虛擬機器的-XX:+PrintGCDetails引數可以列印垃圾收集器的日誌資訊。

-verbose:gc可以檢視Java虛擬機器垃圾收集結果。

相關文章