GC垃圾收集器

垃圾算法是內(nèi)存回收的方法論,垃圾收集器是內(nèi)存回收的具體實(shí)現(xiàn)。


HotSpot虛擬機(jī)的垃圾收集器.png

注:連續(xù)表示可以搭配使用。以下具體介紹幾類垃圾回收器(HotSpot1.7為例)。

一、Serial垃圾收集器(單線程、復(fù)制算法)

  • 是最基本、發(fā)展歷史最悠久的垃圾回收器,使用復(fù)制算法,曾是JDK1.3之前新生代唯一的垃圾收集器。

  • 單線程的收集器,不僅只使用一個(gè)CPU或一條線程區(qū)完成垃圾收集工作,并且在進(jìn)行垃圾回收的同時(shí),必須暫停其他所有的工作線程(Stop the Wolrd)。

  • 圖解


    Serial與Serial Old收集器運(yùn)行過(guò)程.png
  • Serial雖在收集垃圾過(guò)程中需暫停所有其他工作線程,單它簡(jiǎn)單高效,對(duì)于限定單個(gè)CPU來(lái)說(shuō),沒(méi)有線程交互的開(kāi)銷,可獲得最高的單線程垃圾收集效率。所以Serial依然是Java虛擬機(jī)運(yùn)行在Client模式下默認(rèn)的新生代垃圾收集器。

二、ParNew垃圾收集器(Serial+多線程)

  • Serial收集器的多線程版本,使用復(fù)制算法,除了使用多線程進(jìn)行垃圾收集之外,其余行為包括所有控制參數(shù)、收集算法、Stop The World、對(duì)象分配規(guī)則、回收策略等都與Serial相同。
  • 圖解


    ParNew與Serial Old收集器運(yùn)行過(guò)程.png
  • ParNew收集器默認(rèn)開(kāi)啟和CPU數(shù)目相同的線程數(shù),可通過(guò)-XX:ParallelGCThread參數(shù)來(lái)限制垃圾收集器的線程數(shù)。

并行(Parallel):指多條垃圾收集線程并行工作,但此時(shí)用戶線程仍然處于等待狀態(tài)。
并發(fā)(Concurrent):指用戶線程與垃圾收集線程同時(shí)執(zhí)行(但不一定是并行的,可能會(huì)交替執(zhí)行),用戶線程繼續(xù)運(yùn)行,而垃圾收集程序運(yùn)行于另一個(gè)CPU上。

  • ParNew雖然除了多線程外和Serial收集器幾乎完全相同,但它卻是許多運(yùn)行在Server模式下的虛擬機(jī)首選的新生代收集器。其中一個(gè)無(wú)關(guān)性能的原因是除了Serial,只有ParNew能與CMS收集器配合工作。

三、Parallel Scavenge 收集器(多線程復(fù)制算法、高效)

  • 新生代垃圾收集器,使用復(fù)制算法,多線程,它重點(diǎn)關(guān)注的是程序達(dá)到一個(gè)可控制的吞入量(Thoughtput)

吞吐量(Thoughput)
CPU用于運(yùn)行用戶代碼的時(shí)間/CPU總消耗時(shí)間,即吞吐量=運(yùn)行用戶代碼時(shí)間/(運(yùn)行用戶代碼時(shí)間+垃圾收集時(shí)間)。
例如:虛擬機(jī)總共運(yùn)行100分鐘,垃圾收集時(shí)間為1分鐘,那吞吐量為99%。

  • 高吞吐量可以最高效率地利用CPU時(shí)間,盡快地完成程序地運(yùn)算任務(wù),主要適用于后臺(tái)運(yùn)算而不需要太多交互地任務(wù)。
  • 提供了兩個(gè)參數(shù)用于精準(zhǔn)控制吞吐量
    1. -XX:MaxGCPauseMillis:控制最大垃圾收集停頓時(shí)間,允許的值是一個(gè)大于0的毫秒數(shù),收集器盡可能地保證內(nèi)存回收花費(fèi)地時(shí)間不超過(guò)設(shè)定值。
    2. -XX:GCTimeRatio:直接設(shè)置吞吐量大小,值應(yīng)當(dāng)是一個(gè)大于0且小于100的整數(shù),也就是垃圾收集時(shí)間占總時(shí)間的比例,相當(dāng)于吞吐量的倒數(shù)。如果把參數(shù)設(shè)置稱19,那允許的最大GC時(shí)間就占總時(shí)間的5%(即1/(1+19)),默認(rèn)值為99。
  • 也稱為“吞吐量?jī)?yōu)先”收集器。除上述兩個(gè)參數(shù)外,還有一個(gè)參數(shù)-XX:+UseAdaptiveSizePolicy,這是一個(gè)開(kāi)關(guān)參數(shù),當(dāng)這個(gè)參數(shù)打開(kāi)之后,就不需手工指定新生代的大?。?Xmn)、Eden與Survivor區(qū)的比例(-XX:SurvivorRatio)、晉升老年代對(duì)象大小(-XX:PretenureSizeThreshold)等細(xì)節(jié)參數(shù)了,虛擬機(jī)會(huì)根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間或者最大的吞吐量,這種調(diào)節(jié)方式稱為GC自適應(yīng)的調(diào)節(jié)策略(GC Ergonomics)。自適應(yīng)調(diào)節(jié)策略也是Parallel Scavenge收集器與ParNew收集器的一個(gè)重要區(qū)別。
  • 圖解


    Parallel Scavenge與Serial Old收集器運(yùn)行過(guò)程.png

四、Serial Old收集器(單線程、標(biāo)記-整理算法)

  • Serial垃圾收集器老年代不安本,單線程,使用標(biāo)記-整理算法。
  • 主要是運(yùn)行在Client默認(rèn)的java虛擬機(jī)默認(rèn)的老年代垃圾收集器。
  • 在Server模式下,主要有兩個(gè)用途
    1. 在JDK1.5之前版本中與新生代Parallel Scavenge收集器搭配使用。
    2. 作為老年代中使用CMS收集器的后備垃圾收集方案。
  • 圖解


    Serial與Serial Old收集器運(yùn)行過(guò)程.png
ParNew與Serial Old收集器運(yùn)行過(guò)程 - 副本.png

Parallel Scavenge與Serial Old收集器運(yùn)行過(guò)程.png

五、Parallel Old收集器(多線程,標(biāo)記-整理算法)

  • 是Parallel Scavenge的老年代版本,多線程,使用標(biāo)記-整理算法,在JDK1.6才開(kāi)始使用。
  • 在JDK1.6之前,新生代Parallel Scavenge只能搭配老年代的Serial Old,只能保證新生代的吞吐量?jī)?yōu)先,無(wú)法保證整體的吞吐量,Parallel Old正是為了在老年代同樣提供吞吐量?jī)?yōu)先的垃圾收集器,如果系統(tǒng)對(duì)吞吐量要求比較高,可優(yōu)先考慮新生代Parallel Scavenge和老年代Parallel Old收集器的搭配策略。
  • 圖解


    Parallel Scavenge和Parallel Old收集運(yùn)行過(guò)程.png

六、CMS收集器(多線程、標(biāo)記-清除算法)、

  • CMS(Concurrent mark sweep)收集器是一種老年代垃圾收集器,以獲得最短回收停頓時(shí)間為目標(biāo)(可以為交互比較高的程序提高用戶體驗(yàn)),多線程,使用標(biāo)記-清除算法。
  • 它的運(yùn)作過(guò)程比較復(fù)制,分為4個(gè)階段
    1. 初始標(biāo)記(CMS inital mark)
      只是標(biāo)記以下GC Roots能直接關(guān)聯(lián)的對(duì)象,速度很快,仍然需要暫停所有的工作線程。
    2. 并發(fā)標(biāo)記(CMS concurrent mark)
      進(jìn)行GC Roots跟蹤的過(guò)程,和用戶線程一起工作,不需要贊同工作線程。
    3. 重新標(biāo)記(CMS remark)
      為了修正在并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,仍然需要暫停所有的工作線程。
    4. 并發(fā)清除(CMS concurrent sweep)
      清除GC Roots不可達(dá)對(duì)象,和用戶線程一起工作,不需要暫停工作線程。
  • 由于耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除過(guò)程中,垃圾收集線程可以和用戶線程一起并發(fā)工作,所以總體上來(lái)看CMS的收集器的內(nèi)存回收和用戶線程是一起并發(fā)執(zhí)行的。
  • 圖解


    CMS收集器運(yùn)行示意圖.png
  • 三個(gè)缺點(diǎn)
    1. 對(duì)CPU資源非常敏感
      其實(shí)面向并發(fā)設(shè)計(jì)的程序都對(duì)CPU資源比較敏感。CMS默認(rèn)啟動(dòng)的回收線程數(shù)是(CPU數(shù)量+3)/4 ;
    2. 無(wú)法處理浮動(dòng)垃圾(Floating Garbage)
      可能出現(xiàn)“Concurrent Mode Failure”失敗而導(dǎo)致另一次Full GC的產(chǎn)生。由于CMS并發(fā)清理階段用戶線程還在運(yùn)行,伴隨程序運(yùn)行自然還會(huì)有新的垃圾不斷產(chǎn)生,這一部分垃圾出現(xiàn)在標(biāo)記過(guò)程之后,CMS無(wú)法在當(dāng)次收集中處理掉它們,只好留待下一次GC時(shí)在清理掉,這一部分垃圾就稱為“浮動(dòng)垃圾”。
    3. 基于標(biāo)記-清除算法
      收集結(jié)束后會(huì)產(chǎn)生大量的空間碎片。

七、G1收集器

  • Garbage first收集器時(shí)當(dāng)今收集器理論發(fā)展的最前沿成果,一款面向服務(wù)端應(yīng)用的垃圾收集器,與其他GC收集器相比,它具有如下特點(diǎn)。
    1. 并行與并發(fā)
      能充分利用多CPU、多核環(huán)境下的硬件優(yōu)勢(shì),使用多個(gè)CPU來(lái)縮短Stop The World停頓時(shí)間,仍可通過(guò)并發(fā)的方式來(lái)讓Java程序繼續(xù)執(zhí)行。
    2. 分代收集
      雖能獨(dú)立管理GC堆,但它能采用不同的方式區(qū)處理新創(chuàng)建的對(duì)象和已經(jīng)存活了一段時(shí)間的舊對(duì)象以獲取更好的收集效果。
    3. 空間整合
      采用“標(biāo)記-整理”算法,不產(chǎn)生內(nèi)存碎片。
    4. 可預(yù)測(cè)的停頓
      可非常精準(zhǔn)控制停頓時(shí)間,在不犧牲吞吐量的前提下,實(shí)現(xiàn)低停頓垃圾回收。有點(diǎn)實(shí)時(shí)Java(RTSJ)的垃圾收集器的特征。
  • G1避免全區(qū)域垃圾收集,它把堆內(nèi)存劃分為大小固定的幾個(gè)獨(dú)立區(qū)域(Region),并且根據(jù)這些區(qū)域的垃圾收集進(jìn)度,同時(shí)在后臺(tái)維護(hù)一個(gè)優(yōu)先列表,每次根據(jù)所允許的收集時(shí)間,優(yōu)先回收價(jià)值最大(回收所獲得的空間大小以及回收所需時(shí)間的經(jīng)驗(yàn)值)的區(qū)域。區(qū)域劃分和優(yōu)先級(jí)區(qū)域回收機(jī)制,確保G1收集器可以在有限時(shí)間獲得最高的垃圾收集效率。
  • 在G1收集器中,Region之間的對(duì)象引用以及其他收集器中的新生代與老年代之間的對(duì)象引用,JVM都是通過(guò)Remembered Set來(lái)避免全堆掃描的。每個(gè)Region都有與之對(duì)應(yīng)的Remembered Set。當(dāng)進(jìn)行垃圾回收時(shí),在GC根節(jié)點(diǎn)的枚舉范圍中加入Remembered Set即可保證不對(duì)全堆掃描也不會(huì)有遺漏。
  • 如果不計(jì)算Remembered Set操作,G1收集器的運(yùn)作大致可劃分為以下幾個(gè)步驟
    1. 初始標(biāo)記(Initial Marking)
    2. 并發(fā)標(biāo)記(Concurrent Marking)
    3. 最終標(biāo)記(Final Marking)
    4. 篩選回收(Live Data Counting and Evacuation)
  • 圖解


    G1收集器運(yùn)行示意圖.png

理解GC日志

例如如下兩段典型的GC日志:

33.125: [GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs]

100.667: [Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->210K(19456K), [Perm : 2999K->2999K(21248K)],0.0150007 secs] [Times: user=0.01 sys=0.00,real=0.02 secs]

  • "33.125"與“100.667”:代表GC發(fā)生的時(shí)間,這個(gè)數(shù)字的含義時(shí)從JVM啟動(dòng)以來(lái)經(jīng)過(guò)的秒數(shù)。
  • GC日志的開(kāi)頭的“[ GC”和“[ Full GC "說(shuō)明這次垃圾收集的停頓類型,有"Full"表示經(jīng)過(guò)了Stop The World。
  • 接下來(lái)“DefNew”、“Tenured”、“Perm ”表明GC發(fā)生的區(qū)域。
  • 后面方括號(hào)內(nèi)部的“3324K->152K(3712K)”含義是“GC前該內(nèi)存區(qū)域已使用容量->GC后該內(nèi)存區(qū)域已使用容量(該內(nèi)存區(qū)域總?cè)萘浚?/li>
  • 方括號(hào)之外的“3324K->152K(11904K)”表示“GC 前Java堆已使用容量->GC后Java堆已使用容量(Java堆總?cè)萘浚?/li>
  • “0.0025925 secs”表示該內(nèi)存區(qū)域GC所占用的時(shí)間。

垃圾收集器參數(shù)總結(jié)

UseSerial

虛擬機(jī)運(yùn)行在Client模式下的默認(rèn)值,打開(kāi)此開(kāi)關(guān)后,使用Serial+Serial Old的收集器組合進(jìn)行內(nèi)存回收。

UseParNewGC

打開(kāi)此開(kāi)關(guān)后,使用ParNew+Serial Old的收集器組合進(jìn)行內(nèi)存回收。

UseConcMarkSweepGC

打開(kāi)此開(kāi)關(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)值,打開(kāi)此開(kāi)關(guān)后,使用Parallel Scavenge + Serial Old(PS MarkSweep)的收集器組合進(jìn)行內(nèi)存回收。

UserParallelOldGC

打開(kāi)此開(kāi)關(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)持過(guò)一次Minor GC之后,年齡就增加1,當(dāng)超過(guò)這個(gè)參數(shù)值時(shí)就進(jìn)入老年代。

UseAdaptiveSizePolicy

動(dòng)態(tài)調(diào)整Java堆中各個(gè)區(qū)域的大小以及進(jìn)入老年代的年齡。

HandlePromotiveFaliure

是否允許分配擔(dān)保失敗,即老年代的剩余空間不足以應(yīng)付新生代的整個(gè)Eden和Survivor區(qū)的所有對(duì)象都存活的極端情況。

ParallelGCTreads

設(shè)置并行GC時(shí)進(jìn)行內(nèi)存回收的線程數(shù)。

GCTimeRatio

GC時(shí)間占總時(shí)間的比率,默認(rèn)值為99,即允許1%的GC時(shí)間,僅在使用Parallel Scavenge收集器時(shí)生效。

MaxGCPauseMills

設(shè)置GC的最大停頓時(shí)間。僅在使用Parallel Scavenge收集器時(shí)生效。

CMSInitiatingOccupancyFraction

設(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í)生效。

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

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

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