JVM垃圾收集器

原文地址:https://xeblog.cn/articles/24

image

新生代收集器

新生代均采用 復(fù)制 算法來回收內(nèi)存。

Serial 收集器

最基本的、發(fā)展歷史最悠久的單線程的收集器。在進(jìn)行垃圾收集時,必須暫停其它所有的工作線程(Stop The World),直到它收集結(jié)束。它是 JVM 運行在 Client 模式下的默認(rèn)新生代收集器,它比其它單線程的收集器更簡單、更高效??膳c CMS 收集器 配合工作。

ParNew 收集器

Serial 收集器 的多線程版本。它是許多運行在 Server 模式下的虛擬機(jī)首選的新生代收集器,在使用 -XX:+UseConcMarkSweepGC 選項后的默認(rèn)新生代收集器,也可以通過 -XX:+UseParNewGC 選項強(qiáng)制指定它。它除了是多線程收集之外,其它和 Serial 收集器 基本一樣,它默認(rèn)開啟的收集線程數(shù)與 CPU 的數(shù)量相同,在 CPU 非常多的環(huán)境下,可通過 -XX:ParallelGCThreads 參數(shù)限制垃圾收集的線程數(shù)??膳c CMS 收集器 配合工作。

Parallel Scavenge 收集器

并行的多線程收集器,它的目標(biāo)是達(dá)到一個可控的吞吐量(吞吐量是 CPU 用于運行用戶代碼的時間與 CPU 總消耗時間的比值)。

 吞吐量 = 運行用戶代碼的時間 / (運行用戶代碼的時間 + 垃圾收集時間)

高吞吐量可以高效的利用 CPU 時間,盡快的完成程序的運算任務(wù),主要適合在后臺運算而不需要太多交互的任務(wù)。Parallel Scavenge 收集器 提供了兩個參數(shù)用于精確控制吞吐量: -XX:MaxGCPauseMillis-XX:GCTimeRatio。

最大垃圾收集停頓時間:-XX:MaxGCPauseMillis

允許一個大于0的毫秒數(shù),收集器將盡可能保證內(nèi)存回收花費時間不超過這個設(shè)定的值,它是以犧牲吞吐量和新生代空間來縮短 GC 停頓時間的。

吞吐量大?。?XX:GCTimeRatio

允許一個大于0且小于100的整數(shù),垃圾收集時間占總時間的比率。

GC自適應(yīng)調(diào)節(jié)參數(shù):-XX:+UseAdaptiveSizePolicy

這是一個開關(guān)參數(shù),當(dāng)它打開后,就不需要手動指定新生代的大小(-Xmn),EdenSurvivor 區(qū)的比例(-XX:SurvivorRatio)、晉升老年代對象年齡(-XX:PretenureSizeThreshold)等細(xì)節(jié)參數(shù)了,虛擬機(jī)會根據(jù)當(dāng)前系統(tǒng)的運行情況收集性能監(jiān)控信息,動態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時間或最大的吞吐量。

老年代收集器

老年代采用 標(biāo)記-整理標(biāo)記-清除 算法來回收內(nèi)存。

Serial Old 收集器

它是 Serial 收集器 的老年代版本,也是一個單線程收集器,使用 標(biāo)記-整理 算法。這個收集器的主要意義也是在于給 Client 模式下的虛擬機(jī)使用,如果是在 Server 模式下,那么它主要還有兩大用途:

  • JDK1.5 以及之前版本中與 Parallel Scavenge 收集器 搭配使用。
  • 作為 CMS收集器 的后備預(yù)案,在并發(fā)收集發(fā)生 Concurrent Mode Failure 時使用。

Parallel Old 收集器

它是 Parallel Scavenge 收集器 的老年代版本,使用多線程和 標(biāo)記-整理 算法。

CMS 收集器 (Concurrent Mark Sweep)

它是一種并發(fā)的、以獲取最短回收停頓時間為目標(biāo)的收集器,使用 標(biāo)記-清除 算法實現(xiàn)的。運行過程較為復(fù)雜,整個過程分為4個步驟:

  • 初始標(biāo)記(CMS Initial Marking)
  • 并發(fā)標(biāo)記(CMS Concurrent marking)
  • 重新標(biāo)記(CMS ReMarking)
  • 并發(fā)清除(CMS Concurrent Sweep)

初始標(biāo)記、重新標(biāo)記仍需要暫停其它所有的工作線程,初始標(biāo)記僅僅只是標(biāo)記一下 GC Roots 能直接關(guān)聯(lián)到的對象,速度很快。并發(fā)標(biāo)記階段就是進(jìn)行 GC Roots 的可達(dá)性分析過程,而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間因用戶線程繼續(xù)運作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄,這個階段的停頓時間一般會比初始標(biāo)記階段稍長一些,但遠(yuǎn)比并發(fā)標(biāo)記的時間短。CMS 收集器 存在3個明顯的缺點:

  • CMS收集器 會占用一部分線程而導(dǎo)致應(yīng)用程序變慢,總吞吐量會降低。CMS 默認(rèn)啟功的回收線程數(shù)是(CPU數(shù)量 + 3)/ 4 ,相當(dāng)于當(dāng) CPU 在4個以上時,并發(fā)回收時垃圾收集線程不少于 25%CPU 資源,并隨著 CPU 數(shù)量的增加而下降。當(dāng) CPU 不足4個時,CMS 對用戶線程的影響就可能變得更大。
  • CMS收集器 無法處理浮動垃圾,可能出現(xiàn) Concurrent Mode Failure 失敗而導(dǎo)致另一次 Full GC 的產(chǎn)生。由于 CMS 并發(fā)清除階段用戶線程還在運行著,伴隨程序運行自然就還會有新的垃圾不斷產(chǎn)生,這一部分垃圾出現(xiàn)在標(biāo)記過程之后,CMS 無法在當(dāng)次收集中處理掉它們,只好留在下一次 GC 時再清理掉,這一部分垃圾就稱為浮動垃圾。
  • 由于 CMS收集器 使用的是 標(biāo)記-清除 算法,所以垃圾回收后會產(chǎn)生大量的空間碎片。CMS 提供 -XX:+UseCMSCompactAtFullCollection 的開關(guān)參數(shù)(默認(rèn)開啟),用于在 CMS 收集器頂不住要進(jìn)行 Full GC 時開啟內(nèi)存碎片的合并整理過程,這個過程是無法并發(fā)執(zhí)行的,雖然空間碎片沒了,但是停頓時間不得不變長。

全干工程師:G1收集器

G1收集器 是當(dāng)今收集器技術(shù)發(fā)展的最前沿成果之一。

上面所介紹的收集器,都只是負(fù)責(zé) Java堆 中的一部分內(nèi)存(新生代或老年代),而 G1 就不同了,它全干。
G1 將整個 Java堆 劃分為多個大小相等的獨立區(qū)域(Region),雖然還保留有新生代和老年代的概念,但是這兩部分內(nèi)存不再是物理隔離的,它們都是一部分不需要連續(xù)的 Region 的集合了。

具備的特點:

  • 并行與并發(fā):G1 能充分利用多 CPU 、多核環(huán)境下的硬件優(yōu)勢,使用多個 CPU 來縮短 Stop The World 的停頓時間。
  • 分代收集(全干工程師):G1 可以不需要與其它收集器配合就能獨立管理整個 GC堆,根據(jù) 分代收集 的概念采用不同的方式去處理。
  • 空間整合:基于 標(biāo)記-整理復(fù)制 算法,不會產(chǎn)生內(nèi)存空間碎片。
  • 可預(yù)測的停頓:G1 可以建立可預(yù)測的停頓時間模型,將垃圾收集消耗的時間限制在一定的時間內(nèi)。

可預(yù)測的停頓時間模型

G1 之所以能夠建立可預(yù)測的停頓時間模型,是因為它可以有計劃的避免在整個 Java堆 中進(jìn)行全區(qū)域的垃圾收集。G1 通過跟蹤各個 Region 里面的內(nèi)存堆積的價值大?。ɑ厥账@得的空間大小以及回收所需時間的經(jīng)驗值),在后臺維護(hù)一個優(yōu)先回收隊列,每次根據(jù)允許的收集時間,優(yōu)先回收價值最大的 Region。這種使用 Region 劃分內(nèi)存空間以及有優(yōu)先級的區(qū)域回收方式,保證了 G1 收集器在有限的時間內(nèi)可以獲取盡可能高的收集效率。

避免全堆掃描且保證準(zhǔn)確性

G1 收集器中,Region 之間的對象引用以及其它收集器中的新生代與老年代之間的對象引用,虛擬機(jī)都是使用 Remembered Set 來避免全堆掃描的。G1 中的每一個 Region 都有一個與之對應(yīng)的 Remembered Set,在對 Reference 類型的數(shù)據(jù)進(jìn)行寫操作的時間,虛擬機(jī)會產(chǎn)生一個屏障暫時中斷寫操作,然后檢查這個引用的對象是否處于不同的 Region 之中,如果是,就會通過 CardTable 把相關(guān)引用信息記錄到被引用對象所屬的 RegionRemembered Set 中。當(dāng)進(jìn)行內(nèi)存回收時,在 GC Roots 的枚舉范圍內(nèi)加入 Remembered Set 即可保證不對全堆掃描也不會有遺漏。

G1運行過程

如果不計算維護(hù) Remembered Set 的操作,G1 收集器的運行過程大致可以分為4個步驟:

  • 初始標(biāo)記(G1 Initial Marking)
  • 并發(fā)標(biāo)記(G1 Concurrent Marking)
  • 最終標(biāo)記(G1 Final Marking)
  • 篩選回收(G1 Live Data Counting And Evacuation)

初始標(biāo)記階段只是簡單的標(biāo)記一下 GC Roots 能直接關(guān)聯(lián)到的對象,并且修改TAMS(Next Top At Mark Start)的值,讓下一階段用戶程序并發(fā)運行時,能在正確可用的 Region 中創(chuàng)建新對象,這個階段需要停頓線程,但耗時很短。并發(fā)標(biāo)記階段也是進(jìn)行 GC Roots 的可達(dá)性分析過程,耗時很長但是可與用戶程序一起工作。最終標(biāo)記階段是為了修正并發(fā)標(biāo)記期間因用戶線程繼續(xù)運作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄,虛擬機(jī)將這段時間對象變化記錄在線程的 Remembered Set Logs 中,這個階段需要把 Remembered Set Logs 中的數(shù)據(jù)合并到 Remembered Set 里,需要停頓線程,但是可以并行執(zhí)行。
最后的篩選回收階段會對 Region 的回收價值和成本進(jìn)行排序,根據(jù)用戶所期望的 GC 停頓時間來制定回收計劃。

查看JVM所使用的收集器

java -XX:+PrintCommandLineFlags -version

在終端執(zhí)行上述命令后,即可查看 JVM 所使用的收集器。

image

-XX:+UseParallelGC : 是虛擬機(jī)運行在 Server 模式下的默認(rèn)值,打開此開關(guān)后,使用 Parallel Scavenge + Serial Old (PS MarkSweep) 的收集器組合進(jìn)行內(nèi)存回收。

參考

  • 《深入理解Java虛擬機(jī):JVM高級特性與最佳實踐 第二版》
?著作權(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)容