JVM垃圾收集器

如果說收集算法是內(nèi)存回收的方法論,那么垃圾收集器就是內(nèi)存回收的具體實現(xiàn)。

Java虛擬機規(guī)范中對垃圾收集器應(yīng)該如何實現(xiàn)并沒有任何規(guī)定,因此不同的廠商、不同版本的虛擬機所提供的垃圾收集器都可能會有很大差別,并且一般都會提供參數(shù)供用戶根據(jù)自己的應(yīng)用特點和要求組合出各個年代所使用的收集器。

HotSpot JVM中GC的實現(xiàn)主要有以下的幾種:

Serial/Serial Old

ParNew

Parallel Scavenge/Parallel Old

Concurrent Mark Sweep(CMS)

Garbage First(G1)


HotSpot虛擬機的垃圾回收器

圖中展示了7種作用于不同分代的收集器,如果兩個收集器之間存在連線,就說明它們可以搭配使用。虛擬機所處的區(qū)域,則表示它是屬于新生代收集器還是老年代收集器。

7種Java垃圾收集器,錯誤的選擇會對性能影響很大,不同的選擇會導(dǎo)致應(yīng)用的吞吐量(throughtput)和停頓(pause)有巨大的不同。

1. 串行收集器Serial/Serial Old


Serial / Serial Old收集器

Serial收集器:新生代收集器,單線程收集器,串行,使用復(fù)制算法,其它工作線程暫停。

Serial Old收集器:老年代收集器,單線程收集器,串行,使用標記-清理-壓縮算法,其它工作線程暫停(在老年代中進行標記整理算法清理,也需要暫停其它線程)

2. ParNew收集器


ParNew / Serial Old收集器

ParNew收集器:新生代收集器,是Serial收集器的多線程版本,采用復(fù)制算法,多線程進行收集。

ParNew比較重要,它是許多運行在Server模式下的虛擬機中首選的新生代收集器,因為它可以配合CMS收集器一起使用(Parallel Scavenge則不行)。

ParNew收集器在單CPU的環(huán)境中絕對不會有比Serial收集器更好的效果,甚至由于存在線程交互的開銷,該收集器在通過超線程技術(shù)實現(xiàn)的兩個CPU的環(huán)境中都不能百分之百地保證可以超越Serial收集器。

然而,隨著可以使用的CPU的數(shù)量的增加,它對于GC時系統(tǒng)資源的有效利用還是很有好處的。

3. 并行/吞吐優(yōu)先收集器Parallel Scavenge/Parallel Old


Parallel?Scavenge / Parallel Old收集器

Parallel Scavenge:新生代收集器,多線程收集器,使用復(fù)制算法,它與ParNew最主要的區(qū)別是它的目標是吞吐量優(yōu)先而不是時間優(yōu)先(兩者不能兼得)。

所謂吞吐量就是CPU用于運行用戶代碼的時間與CPU運行總時間的比值。吞吐量優(yōu)先適合在后臺完成計算而不需要太多交互的業(yè)務(wù),而時間優(yōu)先適合需要交互和實時性的業(yè)務(wù)。

比如:JVM運行100分鐘,其中運行用戶代碼99分鐘,垃 圾收集1分鐘,則吞吐量是99%,這種收集器能最高效率的利用CPU,適合運行后臺運算

關(guān)注縮短垃圾收集時間的收集器,如CMS,等待時間很少,所以適合用戶交互,提高用戶體驗

Parallel Old:老年代收集器,多線程,并行,使用標記-整理(與Serial Old不同,這里的整理是匯總和壓縮,匯總的意思就是將幸存的對象復(fù)制到預(yù)先準備好的區(qū)域,而不是像Sweep那樣清理廢棄的對象)算法,目標也是吞吐量優(yōu)先,可以與Parallel Scavenge結(jié)合。

4. CMS收集器CMS Collector


CMS收集器

CMS是真正意義上的并發(fā)收集器,作用于老年代。CMS的目標是時間優(yōu)先(最短停頓時間),像服務(wù)器之類的就很適合跑在CMS收集器下,因為互聯(lián)網(wǎng)服務(wù)重視服務(wù)的響應(yīng)速度,希望系統(tǒng)延遲時間短。CMS通常與ParNew配合使用。

CMS是基于標記-清除算法實現(xiàn)的,整個過程分幾步:

初始標記(initial-mark):從GC Root開始,僅掃描與根節(jié)點直接關(guān)聯(lián)的對象并標記,這個過程需要STW,但是GC Root數(shù)量有限,因此時間較短

并發(fā)標記(concurrent-marking):這個階段在初始標記的基礎(chǔ)上繼續(xù)向下進行遍歷標記。這個階段與用戶線程并發(fā)執(zhí)行,因此不停頓

并發(fā)預(yù)清理(concurrent-precleaning):上一階段執(zhí)行期間,會出現(xiàn)一些剛剛晉升老年代的對象,該階段通過重新掃描減少下一階段的工作。該階段并發(fā)執(zhí)行,不停頓

重新標記(remark):重新標記階段會對CMS堆上的對象進行掃描,以對并發(fā)標記階段遭到破壞的對象引用關(guān)系進行修復(fù),以保證執(zhí)行清理之前對象引用關(guān)系是正確的。這一階段需要STW,時間也比較短暫

并發(fā)清理(concurrent-sweeping):清理垃圾對象,這個過程與用戶線程并發(fā)執(zhí)行,不停頓

并發(fā)重置(reset):重置CMS收集器的數(shù)據(jù)結(jié)構(gòu),等待下一次GC

可以看到,整個過程中需要STW的階段僅有初始標記*重新標記階段,所以可以說它的停頓時間比較短(當(dāng)然吞吐量可能會受影響)。

CMS的缺陷

由于CMS是基于標記-清理算法的,因此會產(chǎn)生大量的內(nèi)存碎片。這很可能會出現(xiàn)老年代雖然有大量不連續(xù)的空閑內(nèi)存,但很難找到連續(xù)的內(nèi)存空間來給對象分配,不得不提前觸發(fā)一次Full GC的情況。

針對這一點,CMS提供了一個-XX:+UseCMSCompactAtFullCollection開關(guān)(默認開啟),用于在CMS要gg的時候進行內(nèi)存碎片整理從而得到連續(xù)的內(nèi)存空間。這樣內(nèi)存碎片的問題可以解決,但STW的時間也相應(yīng)變長。

另外,CMS收集器無法處理浮動垃圾(Floating Garbage),可能出現(xiàn)“Concurrent Mode Failure”失敗而導(dǎo)致另一次Full GC的產(chǎn)生。

由于CMS并發(fā)清理階段用戶線程還在運行著,伴隨程序運行自然就還會有新的垃圾不斷產(chǎn)生,這一部分垃圾出現(xiàn)在標記過程之后,CMS無法在當(dāng)次收集中處理掉它們,只好留待下一次GC時再清理掉。這一部分垃圾就稱為“浮動垃圾”。也是由于在垃圾收集階段用戶線程還需要運行,那也就還需要預(yù)留有足夠的內(nèi)存空間給用戶線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集,需要預(yù)留一部分空間提供并發(fā)收集時的程序運作使用。要是CMS運行期間預(yù)留的內(nèi)存無法滿足程序需要,就會出現(xiàn)一次“Concurrent Mode Failure”失敗,這時虛擬機將啟動后備預(yù)案:臨時啟用Serial Old收集器來重新進行老年代的垃圾收集,這樣停頓時間就很長了。

注意并發(fā)(Concurrent)和并行(Parallel)的區(qū)別:

并發(fā)是指用戶線程與GC線程同時執(zhí)行(不一定是并行,可能交替,但總體上是在同時執(zhí)行的),不需要停頓用戶線程(其實在CMS中用戶線程還是需要停頓的,只是非常短,GC線程在另一個CPU上執(zhí)行);

并行收集是指多個GC線程并行工作,但此時用戶線程是暫停的;

所以,Serial是串行的,Parallel收集器是并行的,而CMS收集器是并發(fā)的.

5. G1收集器Garbage First Collector


G1收集器

G1(Garbage First)收集器是HotSpot JVM最新的垃圾收集器,它最大的特點就是將堆內(nèi)存劃分成多個連續(xù)的區(qū)域(region),每個區(qū)域大小相等。因此在G1中新生代與老年代都是由若干個Region組成(不需要連續(xù))。Region的大小是可以重新設(shè)置的。

G1的優(yōu)點:可以非常精確地控制停頓;老年代采用標記-壓縮算法,避免了內(nèi)存碎片的問題。

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

初始標記:初始標記階段僅僅只是標記一下GC Roots能直接關(guān)聯(lián)到的對象,并且修改TAMS(Next Top at Mark Start)的值,讓下一階段用戶程序并發(fā)運行時,能在正確可用的Region中創(chuàng)建新對象,這階段需要停頓線程,但耗時很短。

并發(fā)標記:并發(fā)標記階段是從GC Root開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶程序并發(fā)執(zhí)行。

最終標記:最終標記階段是為了修正在并發(fā)標記期間因用戶程序繼續(xù)運作而導(dǎo)致標記產(chǎn)生變動的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程Remembered Set Logs里面,最終標記階段需要把Remembered Set Logs的數(shù)據(jù)合并到Remembered Set中,這階段需要停頓線程,但是可并行執(zhí)行。

篩選回收:篩選回收階段首先對各個Region的回收價值和成本進行排序,根據(jù)用戶所期望的GC停頓時間來制定回收計劃,這個階段其實也可以做到與用戶程序一起并發(fā)執(zhí)行,但是因為只回收一部分Region,時間是用戶可控制的,而且停頓用戶線程將大幅提高收集效率。

G1會在內(nèi)部維護一個優(yōu)先列表,通過一個合理的模型,計算出每個Region的收集成本和收益期望并量化,這樣每次進行GC時,G1總是會選擇最適合的Region(通常垃圾比較多)進行回收,使GC時間滿足設(shè)置的條件。

Java 8和G1收集器

G1收集器在Java 8 u20上最漂亮的優(yōu)化是String去重(String Deduplication)。String對象和它內(nèi)部使用的char[]數(shù)組會占用比較多的內(nèi)存,因此優(yōu)化過的G1收集器會把重復(fù)的String對象指向同一個char[]數(shù)組,避免多個副本存在在堆里??梢允褂?XX:+UseStringDeduplication參數(shù)來打開這一功能。

參考

JVM垃圾收集器-對比Serial、Parallel、CMS和G1

深入探究JVM | HotSpot JVM的GC實現(xiàn)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容