如果說收集算法是內(nèi)存回收的方法論砍濒,那么垃圾收集器就是內(nèi)存回收的具體實現(xiàn)茧彤。Java虛擬機規(guī)范中對垃圾收集器應該如何實現(xiàn)并沒有任何規(guī)定狮鸭,因此不同廠商饰剥、不同版本的虛擬機所提供的垃圾收集器可能會有很大差別。這里討論的收集器基于JDK 1.7 Update 14之后的HotSpot虛擬機(在這個版本中正式提供了商用的G1收集器蒋歌,之前G1仍然處于試驗狀態(tài)帅掘,JDK11已經(jīng)作為默認的收集器),這個虛擬機所包含的所有收集器如下圖:
一堂油、Serial收集器(新生代)
最原生的收集器, jdk1.3以前唯一的選擇.
單線程收集器. 這里并不是指使用一個CPU或一條收集線程去完成垃圾收集工作, 而是指它在收集垃圾時, 必須暫停用戶工作線程(Stop The World).
單線程
只會使用一個CPU或一條GC線程進行GC,并且在GC過程中暫停其他所有的工作線程,因此用戶的請求或圖形化界面會出現(xiàn)卡頓適合Client模式
一般客戶端應用所需內(nèi)存較小,不會創(chuàng)建太多的對象,而且堆內(nèi)存不大,因此GC時間比較短,即使在這段時間停止一切用戶線程,也不會感到明顯停頓簡單高效
由于Serial收集器只有一條GC線程,避免了線程切換的開銷采用"復制"算法
二修档、ParNew收集器(新生代)
ParNew是Serial的多線程版本
- 多線程并行執(zhí)行
ParNew由多條GC線程并行地進行垃圾清理.
但清理過程仍然需要暫停一切其他用戶線程.
但由于有多條GC線程同時清理,清理速度比Serial有一定的提升 - 適合多CPU服務器的環(huán)境
由于使用多線程,是許多運行在 server 模式下的虛擬機首選的新生代收集器
與Serial性能對比ParNew和Serial唯一區(qū)別就是使用了多線程垃圾回收,在多CPU的環(huán)境下性能比Serial會有一定程度的提升。但線程切換需要額外的開銷,因此在單CPU環(huán)境中表現(xiàn)不如Serial,雙CPU環(huán)境也不一定就比Serial高效府框。默認開啟的收集線程數(shù)與CPU數(shù)量相同吱窝。 - 采用"復制"算法
- 追求“降低停頓時間”
和Serial相比,ParNew使用多線程的目的就是縮短GC時間,從而減少用戶線程被停頓的時間。 - 使用參數(shù)
使用參數(shù) -XX:+UseParNewGC
限制線程數(shù) -XX:ParallelGCThreads
三迫靖、Parallel Scavenge收集器(新生代)
吞吐量優(yōu)先收集器院峡,Parallel Scavenge和ParNew一樣都是并行的多線程、新生代收集器,都使用"復制"算法(Stop-The-World)進行垃圾回收系宜。
ParNew收集器追求降低GC時用戶線程的停頓時間,適合交互式應用,良好的反應速度提升用戶體驗照激。
Parallel Scavenge追求可控的CPU吞吐量,能夠在較短的時間內(nèi)完成指定任務,適合不需太多交互的后臺運算。
- 優(yōu)點:可以精確控制吞吐量
- 缺點:原本10s收集一次, 每次停頓100ms, 設置完參數(shù)之后可能變成5s收集一次, 每次停頓70ms. 停頓時間變短, 但收集次數(shù)變多
四盹牧、Serial Old收集器(老年代)
Serial的老年代版本,都是單線程收集器,GC時只啟動一條GC線程,因此都適合客戶端應用.
它們唯一的區(qū)別就是:Serial Old工作在老年代,使用"標記-整理"算法俩垃;Serial工作在新生代,使用"復制"算法。
五汰寓、Parallel Old收集器(老年代)
Parallel Scavenge收集器的老年代版本口柳。
在jdk1.6之前, 如果新生代選擇了Parallel Scaenge收集器, 老年代除了Serial Old(PS Mark Sweep)收集器外別無選擇.( 上面說過, Parallel Scavenge收集器無法與CMS-Concurrent Mark Sweep收集器搭配工作)。但是現(xiàn)在可以使用Parallel Scavenge + Parallel Old組合. 而不必像之前那樣Prallel Scavenge + Serial Old組合.
六有滑、CMS收集器(老年代)
(Concurrent Mark Sweep Collector) : 低延遲為先!
回收停頓時間比較短跃闹、目前比較常用的垃圾回收器。它通過初始標記(InitialMark)俺孙、并發(fā)標記(Concurrent Mark)、重新標記( Remark)掷贾、并發(fā)清除( Concurrent Sweep )四個步驟完成垃圾回收工作睛榄。
有兩步需要"Stop The World":初始標記和重新標記。
- 初始標記 (Initial Mark)
停止一切用戶線程,僅使用一條初始標記線程對所有與GC Roots直接相關聯(lián)的 老年代對象進行標記,速度很快 - 并發(fā)標記 (Concurrent Marking Phase)
使用多條并發(fā)標記線程并行執(zhí)行,并與用戶線程并發(fā)執(zhí)行.此過程進行可達性分析,標記所有這些對象可達的存貨對象,速度很慢 - 重新標記 ( Remark)
因為并發(fā)標記時有用戶線程在執(zhí)行想帅,標記結果可能有變化
停止一切用戶線程,并使用多條重新標記線程并行執(zhí)行,重新遍歷所有在并發(fā)標記期間有變化的對象進行最后的標記.這個過程的運行時間介于初始標記和并發(fā)標記之間 - 并發(fā)清除 (Concurrent Sweeping)
只使用一條并發(fā)清除線程,和用戶線程們并發(fā)執(zhí)行,清除剛才標記的對象
這個過程非常耗時
CMS的缺點:
- 吞吐量低
由于CMS在GC過程用戶線程和GC線程并行,從而有線程切換的額外開銷
因此CPU吞吐量就不如在GC過程中停止一切用戶線程的方式來的高 - 無法處理浮動垃圾,導致頻繁Full GC
由于垃圾清除過程中,用戶線程和GC線程并發(fā)執(zhí)行,也就是用戶線程仍在執(zhí)行,那么在執(zhí)行過程中會產(chǎn)生垃圾,這些垃圾稱為"浮動垃圾"
如果CMS在GC過程中,用戶線程需要在老年代中分配內(nèi)存時發(fā)現(xiàn)空間不足,就需再次發(fā)起Full GC,而此時CMS正在進行清除工作,因此此時只能由Serial Old臨時對老年代進行一次Full GC - 使用"標記-清除"算法產(chǎn)生碎片空間
由于CMS使用了"標記-清除"算法, 因此清除之后會產(chǎn)生大量的碎片空間,不利于空間利用率.不過CMS提供了應對策略:
1场靴、開啟-XX:+UseCMSCompactAtFullCollection
開啟該參數(shù)后,每次FullGC完成后都會進行一次內(nèi)存壓縮整理,將零散在各處的對象整理到一塊兒.但每次都整理效率不高,因此提供了以下參數(shù).
2、設置參數(shù)-XX:CMSFullGCsBeforeCompaction
本參數(shù)告訴CMS,經(jīng)過了N次Full GC過后再進行一次內(nèi)存整理.
CMS應用場景:
目前很大一部分的Java應用集中在互聯(lián)網(wǎng)網(wǎng)站或B/S系統(tǒng)的服務端上,這類應用尤其重視服務的響應速度旨剥,希望系統(tǒng)停時間最短咧欣,以給用戶帶來較好的體驗。CMS收集器非常符合這類應用的需求轨帜。
七魄咕、G1收集器(萬能收集器)
Hotspot 在JDK7中推出了新一代 G1 ( Garbage-First Garbage Collector )垃圾回收,通過
-XX:+UseG1GC
參數(shù)啟用
和CMS相比蚌父,Gl具備壓縮功能哮兰,能避免碎片向題,G1的暫停時間更加可控苟弛。性能總體還是非常不錯的,G1是當今最前沿的垃圾收集器成果之一.
G1收集器相關概念
- G1的內(nèi)存模型
沒有新生代和老年代的概念,而是將Java堆劃分為一塊塊獨立的大小相等的Region.
當要進行垃圾收集時,首先估計每個Region中的垃圾數(shù)量,每次都從垃圾回收價值最大的Region開始回收,因此可以獲得最大的回收效率 - Remembered Set
一個對象和它內(nèi)部所引用的對象可能不在同一個Region中,那么當垃圾回收時,是否需要掃描整個堆內(nèi)存才能完整地進行一次可達性分析?
當然不是,每個Region都有一個Remembered Set,用于記錄本區(qū)域中所有對象引用的對象所在的區(qū)域,從而在進行可達性分析時,只要在GC Roots中再加上Remembered Set即可防止對所有堆內(nèi)存的遍歷.
G1收集器特點:
- 并行與并發(fā):G1能充分利用多CPU喝滞,多核環(huán)境下的硬件優(yōu)勢,使用多個CPU來縮短Stop-The-World停頓時間膏秫,部分其他收集器原本需要停頓Java線程執(zhí)行的GC動作右遭,G1收集器仍然可以通過并發(fā)的方式讓Java程序繼續(xù)執(zhí)行。
- 分代收集:與其他收集器一樣缤削,分代概念在G1中得以保留窘哈。
- 空間整合:與CMS的“標記-清理”算法不同,G1從整體來看是基于“標記-整理”算法實現(xiàn)的收集器,從局部上來看是基于“復制”算法實現(xiàn)的目派,這兩種算法都不會產(chǎn)生內(nèi)存空間碎片岔留。
- 可預測的停頓:這是G1相對于CMS的另一大優(yōu)勢,降低停頓時間是G1和CMS共同關注點满哪,但G1除了追求低停頓外,還能建立可預測的停頓時間模型劝篷,能讓使用者明確指定在一個長度為M毫秒的時間片段內(nèi)哨鸭,消耗在垃圾收集器上的時間不得超過N毫秒,這幾乎已經(jīng)是實時Java(RTSJ)的垃圾收集器的特征了娇妓。
G1垃圾收集過程
- 初始標記
標記與GC Roots直接關聯(lián)的對象,停止所有用戶線程,只啟動一條初始標記線程,這個過程很快. - 并發(fā)標記
進行全面的可達性分析,開啟一條并發(fā)標記線程與用戶線程并行執(zhí)行.這個過程比較長. - 最終標記
標記出并發(fā)標記過程中用戶線程新產(chǎn)生的垃圾.停止所有用戶線程,并使用多條最終標記線程并行執(zhí)行. - 篩選回收
回收廢棄的對象.此時也需要停止一切用戶線程,并使用多條篩選回收線程并行執(zhí)行.
S0/S1的功能由G1中的Survivor region來承載,通過GC日志可以觀察到完整的垃圾回收過程如下像鸡,其中就有Survivor regions的區(qū)域從0個到1個
紅色標識的為G1中的四種region,都處于Heap中.
G1執(zhí)行時使用4個worker并發(fā)執(zhí)行,在初始標記時哈恰,還是會觸發(fā)STW,如第一步所示的Pause