一起聊聊JVM中的垃圾收集器
堆上的對(duì)象被判斷未無(wú)用對(duì)象之后,JVM就會(huì)執(zhí)行垃圾清理來(lái)釋放內(nèi)存給后續(xù)的創(chuàng)建新對(duì)象使用。
圖片
垃圾收集算法(標(biāo)記-清除算法、標(biāo)記-復(fù)制算法、標(biāo)記-整理算法)是內(nèi)存回收的方法論,那么垃圾收集器就是內(nèi)存回收的具體實(shí)現(xiàn),下圖是市面上常見的垃圾收集器:
圖片
新生代收集器:Serial、ParNew、Parallel Scavenge;老年代收集器:SerialOld、CMS、Parallel Old;同時(shí)支持新生代和老年代收集器:G1、ZGC、Shenandoah。
JDK9之前是新生代和老年代搭配使用;JDK1.9開始默認(rèn)是G1垃圾收集器。在G1之前是分成新生代和老年代,新生代和老年代使用不同的垃圾收集器,因?yàn)樾律屠夏甏乩膱?chǎng)景不同,所以使用不同的垃圾收集器。
1、新生代垃圾收集器
1.1 serial垃圾收集器
Serial收集器是歷史最悠久也是最基礎(chǔ)垃圾收集器,它是一個(gè)單線程工作的收集器,其在進(jìn)行垃圾收集時(shí)必須暫停其他所有工作線程(用戶線程)直到垃圾收集結(jié)束,這也就是STW(stop the world)。
STW會(huì)造成在用戶正常使用系統(tǒng)時(shí)候把用戶的正常工作的線程全部停掉,進(jìn)而造成卡頓的現(xiàn)象給用戶的體驗(yàn)是相當(dāng)不好的。如下是Serial收集器工作的原理圖:
圖片
垃圾回收不是想什么時(shí)候做就可以立即觸發(fā)的,而是需要等待所有的線程運(yùn)行到安全點(diǎn)后才可以觸發(fā)。
安全點(diǎn)是指代碼中一些特定的位置,當(dāng)線程運(yùn)行到這些位置時(shí)它的狀態(tài)才是確定的,這樣JVM就可以安全的進(jìn)行一些操作(如GC)。安全點(diǎn)位置主要有:
(1)方法返回之前
(2)調(diào)用某方法之后
(3)拋出異常的位置
(4)循環(huán)的末尾
當(dāng)垃圾收集需要中斷線程的時(shí)候不是直接對(duì)線程操作,僅僅是簡(jiǎn)單的設(shè)置一個(gè)標(biāo)志位,各個(gè)線程執(zhí)行過(guò)程時(shí)會(huì)不停的主動(dòng)去輪詢這個(gè)標(biāo)志,一旦發(fā)現(xiàn)中斷標(biāo)識(shí)為true時(shí)就會(huì)自己在最近的安全點(diǎn)上主動(dòng)中斷掛起。輪詢標(biāo)志的地方和安全點(diǎn)是重合的。
假設(shè)一個(gè)線程處于sleep或中斷狀態(tài),那么此時(shí)它就不能響應(yīng)JVM的中斷請(qǐng)求來(lái)運(yùn)行到安全點(diǎn),因此JVM引入了safe Region(在一段代碼片段中引用關(guān)系不會(huì)發(fā)生變化,這個(gè)區(qū)域內(nèi)的任意地方開始GC都是安全的)
serial垃圾收集器采用的是復(fù)制算法,其特點(diǎn)是簡(jiǎn)單而高效,但是如果內(nèi)存很大的時(shí)候,單線垃圾收集就會(huì)使得STW時(shí)間變得很長(zhǎng)。
1.2 ParNew垃圾收集器
ParNew收集器實(shí)質(zhì)上是Serial收集器的多線程并行版本,除了同時(shí)使用多條線程進(jìn)行垃圾收集之外,ParNew和Serial收集器可用的所有控制參數(shù)(如:-XXSurvivorRatio)、收集算法、對(duì)象分配規(guī)則、回收策略等都完全一致,如下是 ParNew收集器得工作原理圖:
圖片
ParNew收集器是運(yùn)行在Server模式下的虛擬機(jī)的首選垃圾收集器,ParNew除了Serial收集器搭配使用外,它能與CMS收集器配合工作。
1.3 Parallel Scavenge收集器
Parallel Scavenge收集器是基于標(biāo)記-復(fù)制算法實(shí)現(xiàn)的收集器,也是能夠并行收集的多線程收集器,如下是其工作得原理圖:
圖片
Parallel Scavenge收集器的目標(biāo)是達(dá)到一個(gè)可控制的吞吐量(Throughput)。吞吐量是處理器用于運(yùn)行用戶代碼的時(shí)間與處理器總消耗時(shí)間的比值,如下是其計(jì)算得方式:
吞吐量= 運(yùn)行用戶代碼時(shí)間 / (運(yùn)行用戶代碼時(shí)間+運(yùn)行垃圾收集時(shí)間)如虛擬機(jī)需要完成任務(wù)A,用戶代碼加上垃圾收集總共耗費(fèi)了10分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是90%。
另外Parallel Scavenge收集器提供了兩個(gè)參數(shù)用于精確控制吞吐量,如下所示:
參數(shù)  | 含義  | 
-XX:MaxGCPauseMillis  | 控制最大垃圾收集停頓時(shí)間,允許的值是一個(gè)大于0的毫秒數(shù),收集器將盡力保證內(nèi)存回收花費(fèi)的時(shí)間不超過(guò)用戶設(shè)定值  | 
-XX:GCTimeRatio  | 設(shè)置吞吐量大小,值應(yīng)當(dāng)是一個(gè)大于0和小于100的整數(shù),也就是垃圾收集時(shí)間占總時(shí)間的比率  | 
縮短垃圾收集停頓時(shí)間是以犧牲吞吐量和新生代空間為代價(jià)換取的,系統(tǒng)把新生代調(diào)得小一些,收集100MB新生代肯定比收集1G空間快,但是這樣回導(dǎo)致垃圾收集發(fā)生得更頻繁,如原來(lái)60秒收集一次、每次停頓100毫秒,現(xiàn)在變成20秒收集一次、每次停頓 50毫秒,這樣停頓時(shí)間的確在下降,但吞吐量也降下來(lái)了。
直接設(shè)置吞吐量大小,相當(dāng)于吞吐量的倒數(shù)。如設(shè)置GCTimeRatio為 99,那么就允許最大1%的垃圾收集時(shí)間。
Parallel Scavenge收集器與吞吐量關(guān)系密切,所以它也被稱作“吞吐量?jī)?yōu)先收集器”。
2、老年代垃圾收集器
2.2 serial old垃圾收集器
Serial Old是Serial收集器的老年代版本,它同樣是一個(gè)單線程收集器,使用標(biāo)記-整理算法。如下是 Serial Old收集器工作原理:
圖片
Serial Old收集器供客戶端模式下的HotSpot虛擬機(jī)使用;在服務(wù)端模式下,主要的用途如下:
(1)在JDK5以及之前的版本中與Parallel Scavenge 收集器搭配使用,(2)作為CMS收集器發(fā)生失敗時(shí)的后備預(yù)案,即就是在并發(fā)收集發(fā)生Concurrent Mode Failure時(shí)使用。
2.2 Parallel Old 收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,它是基于標(biāo)記-整理算法實(shí)現(xiàn)的多線程并發(fā)收集,如下是其工作的原理圖:
圖片
在注重吞吐量或CPU資源的場(chǎng)景下,都可以優(yōu)先考慮Parallel Scavenge加Parallel Old 收集器組合來(lái)進(jìn)行垃圾收集。由于Parallel Old底層采用的標(biāo)記-整理算法,此時(shí)STW時(shí)間更長(zhǎng)一些,如下是標(biāo)記-整理算法的工作示意圖:
圖片
    垃圾標(biāo)記出來(lái)后,不但要清除垃圾而且還要將存活的對(duì)象移動(dòng)整理到內(nèi)存的一側(cè)。
2.3 CMS
CMS(Concurrent Mark Sweep)基于標(biāo)記-清除算法實(shí)現(xiàn)的,它以減少停頓時(shí)間(STW)為目標(biāo)的垃圾收集器,主要用于處理老年代的垃圾回收任務(wù),如下是CMS的工作原理圖:
圖片
CMS垃圾收集器執(zhí)行工作過(guò)程相對(duì)于其他垃圾收集器來(lái)說(shuō)要更復(fù)雜一些,整個(gè)過(guò)程分為如下的幾個(gè)過(guò)程:
(1)初始標(biāo)記
標(biāo)記GC Roots能直接關(guān)聯(lián)到的對(duì)象,雖然速度很快,但是這個(gè)過(guò)程會(huì)發(fā)生STW。
(2)并發(fā)標(biāo)記
從GC Roots 的直接關(guān)聯(lián)對(duì)象開始遍歷整個(gè)對(duì)象圖的過(guò)程,這個(gè)過(guò)程耗時(shí)較長(zhǎng)但是不需要停頓用戶線程,可以與垃圾收集線程一起并發(fā)運(yùn)行。
(3)重新標(biāo)記
為了修正并發(fā)標(biāo)記期間,因用戶序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,這個(gè)階段也會(huì)暫停應(yīng)用線程,但耗時(shí)較短。
(4)并發(fā)清除
CMS開始并發(fā)的清除已經(jīng)被判斷為已經(jīng)死亡的對(duì)象,釋放內(nèi)存空間。這個(gè)階段同樣是與應(yīng)用程序并行執(zhí)行的,不會(huì)停止應(yīng)用線程。
CMS垃圾收集器具有并發(fā)收集、低停頓的特點(diǎn),但是CMS垃圾收集器對(duì)CPU資源敏感、同時(shí)CMS使用的是標(biāo)記-清除算法清理垃圾,容易產(chǎn)生內(nèi)存碎片。
CMS在并發(fā)階段并沒(méi)有STW,所以用戶線程可能會(huì)產(chǎn)生更多垃圾,此時(shí)就可能出現(xiàn)“Concurrent Mode Failure”失敗進(jìn)而導(dǎo)致一次STW的Full GC,然后使用serial old垃圾回收器收集垃圾。
總結(jié):
(1)新生代的垃圾回收器有serial、ParNew、Parallel Scavenge;老年大垃圾收集器有Serial Old、CMS、Parallel Old;新生代和老年代收集器:G1、ZGC、Shenandoah。
(2)JDK8中默認(rèn)的垃圾收集器組合是Parallel Scavenge+Parallel Old;JDK9默認(rèn)是G1垃圾收集器。
(3)CMS是基于標(biāo)記-清除算法實(shí)現(xiàn)的,Parallel Old基于標(biāo)記-整理算法實(shí)現(xiàn)的。
(4)JDK8中ParNew+CMS組合適用于需要低停頓時(shí)間的應(yīng)用,典型的應(yīng)用如電商網(wǎng)站、在線游戲、高并發(fā)服務(wù)器。
(5)JDK8中Parallel Scavenge+Parallel Old適用于多核處理器的高吞吐量應(yīng)用,如科學(xué)計(jì)算、數(shù)據(jù)分析、大規(guī)模數(shù)據(jù)處理等場(chǎng)景。















 
 
 













 
 
 
 