理解JVM之垃圾收集器詳解
目錄
- 前言
- 一梯刚、Serial收集器
- 二、ParNew收集器
- 三薪寓、Parallel Scavenge收集器
- 四亡资、Serial Old收集器
- 五澜共、Parallel Old收集器
- 六、CMS收集器
- 七锥腻、 G1收集器
- 附:垃圾收集器參數(shù)匯總
前言
垃圾收集器作為內(nèi)存回收的具體表現(xiàn)嗦董,Java虛擬機(jī)規(guī)范并未對(duì)垃圾收集器的實(shí)現(xiàn)做規(guī)定,因而不同版本的虛擬機(jī)有很大區(qū)別瘦黑,因而我們?cè)谶@里主要討論基于Sun HotSpot虛擬機(jī)1.6版本Update22,此虛擬機(jī)包含的收集器如下所示:
如圖展示了7種作用于不同分代的收集器京革,若兩個(gè)收集器之間存在連線,說明他們可以搭配使用幸斥。我們堆收集器進(jìn)行比較就是為了針對(duì)具體的情況選擇最合適的收集器匹摇。
回到頂部
一、Serial收集器
Serial是最基本甲葬,最早的收集器廊勃,曾是JDK1.3.1之前的虛擬機(jī)新生代唯一選擇,這個(gè)收集器是單線程收集器经窖,它不僅僅只會(huì)使用一個(gè)單線程或一條收集線程區(qū)完成垃圾收集工作坡垫,更重要的是當(dāng)進(jìn)行垃圾收集時(shí),其他工作線程必須暫停画侣,直到收集結(jié)束冰悠。下圖為Serial/Serial Old收集器運(yùn)行過程:
從JDK1.3到JDK1.7,HoSpot虛擬機(jī)開發(fā)團(tuán)隊(duì)一直在為消除或減少工作線程因內(nèi)存回收而導(dǎo)致停頓的現(xiàn)象而努力著,從Serial收集器配乱,到Parallel收集器屿脐,再到Concurrent Mark Sweep(CMS)用戶線程停頓時(shí)間不斷縮減,但此現(xiàn)象并未完全消除宪卿。
到目前為止的诵,Serial是虛擬機(jī)運(yùn)行在Client模式下的默認(rèn)的新生代收集器,它也有很多優(yōu)點(diǎn):簡(jiǎn)單高效(與其他收集器單線程相比)佑钾,對(duì)于限定單個(gè)CPU西疤,由于沒有線程交互開銷,垃圾收集獲得最高的單線程收集效率休溶。Serial收集器是運(yùn)行在Client模式下的虛擬機(jī)的好的選擇代赁。
二、ParNew收集器
ParNew收集器是Serial收集器的多線程版本兽掰,除使用多線程進(jìn)行垃圾收集外芭碍,其余行為包括扣Serial收集器可用的控制參數(shù)、收集算法孽尽、Stop The World 窖壕、對(duì)象分配規(guī)則、回收策略等與Serial完全一樣。ParNew收集器的工作過程如下圖所示:
ParNew是運(yùn)行在Server模式下虛擬機(jī)種的首選新生代收集器瞻讽,因?yàn)槌薙erial收集器鸳吸,目前只有它能夠與CMS收集器配合工作。ParNew收集器由于存在線程交互速勇,可能不會(huì)有比Serial更好的效果但隨著CPU數(shù)量的增加晌砾,它對(duì)于GC時(shí)系統(tǒng)資源的利用有很大好處。
此處區(qū)分以下并行和并發(fā):
并行(Parallel):多條垃圾收集線程并行工作烦磁,此時(shí)用戶線程仍處于等待狀態(tài)养匈。
并發(fā)(Concurrent):指用戶線程與垃圾收集線程同時(shí)執(zhí)行(不一定并行,可能交替執(zhí)行)都伪,用戶程序繼續(xù)運(yùn)行乖寒,垃圾收集程序運(yùn)行在另一個(gè)CPU上。
三院溺、Parallel Scavenge收集器
Parallel Scavenge收集器是一個(gè)新生代收集器,它時(shí)復(fù)制算法的收集器磅轻,也是并行的多線程收集器珍逸,它的關(guān)注點(diǎn)與其他收集器不同,其它收集器盡可能縮短用戶線程停頓時(shí)間,Parallel Scavenge收集器則是達(dá)到一個(gè)可控制的吞吐量(吞吐量=運(yùn)行用戶代碼時(shí)間/(運(yùn)行代碼時(shí)間+垃圾收集時(shí)間))聋溜。停頓時(shí)間越短越適合需要用戶交互程序谆膳,良好相應(yīng)速度能提升用戶體驗(yàn);高吞吐量則可以最高效率的利用CPU時(shí)間撮躁,盡快完成程序任務(wù)漱病,適用于后臺(tái)運(yùn)算而不需要太多交互的任務(wù)。
四把曼、Serial Old收集器
Serial Old是Serial的老年版本杨帽,它也是單線程收集器,使用“標(biāo)記-整理“算法嗤军。它主要是被Client模式下的虛擬機(jī)使用注盈。在Server模式下,它有兩個(gè)用途:一是在JDK1.5及之前的本本種與Parallel Scavenge收集器搭配使用叙赚;二是作為CMS收集器的后備份預(yù)案老客,在并發(fā)收集發(fā)生Corrent Model Failure時(shí)候使用。Serial Old的工作過程如下圖所示:
五震叮、Parallel Old收集器
Parallel Old使用多線程和“標(biāo)記-整理”算法胧砰,于JDK1.6開始提供,是Parallel Scavenge收集器的老年代版本苇瓣。在其出現(xiàn)前尉间,老年代除了Serial old外別無選擇,由于Serial old的“拖累”,使用Parallel Scavenge未必能夠在整體上實(shí)現(xiàn)吞吐量最大化的效果乌妒。Parallel Old出現(xiàn)后汹想,“吞吐量”的黃金組合出現(xiàn)了,在注重吞吐量及CPU資源的敏感場(chǎng)合撤蚊,優(yōu)先考慮Parallel Old與Parallel Scavenge收集器組合古掏。Parallel Old工作過程如圖 所示:
六、CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器侦啸。CMS收集器應(yīng)用于那些重視服務(wù)響應(yīng)速度槽唾,系統(tǒng)停頓短的場(chǎng)景。CMS應(yīng)用了“標(biāo)記-清除”算法光涂,他的運(yùn)作過程包括四個(gè)場(chǎng)景:
- 初始標(biāo)記(CMS initial mark)
- 并發(fā)標(biāo)記(CMS concurrent mark)
- 重新標(biāo)記(CMS ermark)
- 并發(fā)清除(CMS concurrent sweep)
由于在整個(gè)過程中耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記與并發(fā)清除過程收集器線程可與用戶線程一起工作庞萍,因而,可看做并發(fā)標(biāo)記與并發(fā)清除的收集器線程是與用戶線程一起并發(fā)執(zhí)行的忘闻,其運(yùn)行過程如下圖所示:
CMS是一款優(yōu)秀的收集器钝计,他的主要優(yōu)點(diǎn)有:并發(fā)收集,低停頓齐佳,因而也稱為并發(fā)低停頓收集器私恬,但其仍有缺點(diǎn),主要表現(xiàn)在三個(gè)方面:
- CMS收集器對(duì)CPU資源非常敏感炼吴。面向并發(fā)設(shè)計(jì)的程序都對(duì)CPU比較敏感本鸣,在并發(fā)階段,它會(huì)占用一部分線程導(dǎo)致應(yīng)用程序變慢硅蹦,降低總吞吐量荣德。CMS默認(rèn)自動(dòng)回收線程數(shù)為(cpu數(shù)+3)/4,故當(dāng)cpu總數(shù)小于4 時(shí)童芹,幾乎要拿出50%的資源執(zhí)行線程收集器涮瞻。增量式并發(fā)收集器(i-CMS)就是為解決這種情況而創(chuàng)造的,他的思想是:并發(fā)標(biāo)記和并發(fā)清理時(shí)假褪,讓GC線程饲宛、用戶線程交替運(yùn)行,盡量減少GC獨(dú)占資源時(shí)間嗜价,雖然使得垃圾回收時(shí)間變長(zhǎng)艇抠,但降低了對(duì)用戶程序的影響。目前久锥,i-CMS已經(jīng)不再提倡用戶使用家淤。
- CMS收集器無法處理浮動(dòng)垃圾。當(dāng)出現(xiàn)“Concurrent Mode Failure”導(dǎo)致另一次Full GC的產(chǎn)生瑟由。此時(shí)絮重,CMS并發(fā)清理階段用戶線程還在運(yùn)行著,無法集中處理運(yùn)行程序產(chǎn)生的垃圾。由于垃圾收集階段用戶線程還在運(yùn)行青伤,需給線程預(yù)留內(nèi)存督怜,要是預(yù)留的內(nèi)存無法滿足需求,就會(huì)出現(xiàn)“Concurrent Mode Failure”失敗狠角,此時(shí)号杠,臨時(shí)啟用Serial Old進(jìn)行老年代垃圾回收,耗費(fèi)時(shí)間過長(zhǎng)丰歌。
- CMS算法是基于“標(biāo)記-清除”的收集器姨蟋,收集借書產(chǎn)生大量的空間碎片,當(dāng)無法找到足夠的連續(xù)空間存放對(duì)象就會(huì)觸動(dòng)Full GC立帖。未解決這個(gè)問題眼溶,CMS增加一個(gè)碎片整理時(shí)間,此過程無法并發(fā)晓勇,停頓時(shí)間變長(zhǎng)堂飞。
七、 G1收集器
G1收集器與CMS相比有兩點(diǎn)改進(jìn):一是G1是基于“標(biāo)記-整理”的收集器绑咱,不會(huì)產(chǎn)生空間碎片绰筛。二是他可以精確地控制停頓,能讓使用者指定在一個(gè)時(shí)間片段內(nèi)消耗在垃圾收集上的時(shí)間羡玛。G1可以在不犧牲吞吐量的前提下完成低停頓 內(nèi)存回收。G1將java堆劃分為多個(gè)大小獨(dú)立的區(qū)域宗苍,并根據(jù)區(qū)域的垃圾堆積程度稼稿,維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)收集時(shí)間讳窟,優(yōu)先回收垃圾最多的區(qū)域让歼,保證來G1收集器在有效時(shí)間內(nèi)的的回收效率。
附:垃圾收集器參數(shù)匯總
參數(shù) | 描述 |
---|---|
UseSerialGC | 虛擬機(jī)運(yùn)行在Client模式下的默認(rèn)值丽啡,打開此開關(guān)谋右,使用Serial+Serial Old收集器組合進(jìn)行內(nèi)存回收 |
UseParNewGC | 打開此開關(guān)后,使用ParNew+Serial Old的收集器組合進(jìn)行內(nèi)存回收 |
UseConcMarkSweepGC | 打開此開關(guān)后补箍,使用ParNew+CMS+Serial Old的收集器組合進(jìn)行內(nèi)存回收改执。Serial Old收集器將作為CMS收集器出現(xiàn)Concurrent Mode Failure失敗后的后備收集器使用 |
UseParallelGC | 虛擬機(jī)運(yùn)行在Server模式下的默認(rèn)值,打開此開關(guān)后坑雅,使用Parallel Scavenge + Serial Old(PS MarkSweep)的收集器組合進(jìn)行內(nèi)存回收 |
UseParallelOldGC | 打開此開關(guān)后辈挂,使用Parallel Scavenge + Parallel Old的收集器組合進(jìn)行內(nèi)存回收 |
SurvivorRatio | 新生代中Eden區(qū)域與Survivor區(qū)域的容量比值,默認(rèn)值為8裹粤,代表Eden:Survivor=8:1 |
PretenureSizeThreshold | 直接晉升到老年代的對(duì)象大小终蒂,設(shè)置這個(gè)參數(shù)后,大于這個(gè)參數(shù)的對(duì)象將直接在老年代分配 |
MaxTenuringThreshold | 晉升到老年代的對(duì)象年齡,每個(gè)對(duì)象在堅(jiān)持過一次Minor GC之后拇泣,年齡就增加1噪叙,當(dāng)超過這個(gè)參數(shù)時(shí)就進(jìn)入老年代 |
UseAdaptiveSizePolicy | 動(dòng)態(tài)調(diào)整Java堆中各個(gè)區(qū)域的大小以及進(jìn)入老年代的年齡 |
HandlePromotionFailure | 是否允許分配擔(dān)保失敗,即老年代的剩余空間不足以應(yīng)付新生代的整個(gè)Eden和Survivor區(qū)的所有對(duì)象都存活的極端情況 |
ParallelGCThreads | 設(shè)置并行GC時(shí)進(jìn)行內(nèi)存回收的線程數(shù) |
GCTimeRatio | GC時(shí)間占總時(shí)間的比率霉翔,默認(rèn)值為99睁蕾,即允許1%的GC時(shí)間。僅在使用Parallel Scavenge收集器時(shí)生效 |
MaxGCPauseMillis | 設(shè)置GC的最大停頓時(shí)間早龟,僅在使用Parallel Scavenge收集器時(shí)生效 |
CMSInitingOccupancyFraction | 設(shè)置CMS收集器在老年代空間被使用多少后觸發(fā)垃圾收集惫霸。默認(rèn)值為68%,僅在使用CMS收集器時(shí)生效 |
UseCMSCompactAtFullCollection | 設(shè)置CMS收集器在完成垃圾收集后是否要進(jìn)行一次內(nèi)存碎片整理葱弟,僅在使用CMS收集器時(shí)生效 |
CMSFullGCsBeforeCompaction | 設(shè)置CMS收集器在進(jìn)行若干次垃圾收集后再啟動(dòng)一次內(nèi)存碎片整理壹店。僅在使用CMS收集器時(shí)生效 |
本文主要參考自《深入理解Java虛擬機(jī)——JVM高級(jí)特性與最佳實(shí)踐》一書