柚子快報(bào)邀請碼778899分享:java JVM(四)
柚子快報(bào)邀請碼778899分享:java JVM(四)
目錄
垃圾回收算法垃圾回收算法的評價(jià)標(biāo)準(zhǔn)吞吐量(Throughput)最大暫停時(shí)間堆使用效率
算法權(quán)衡標(biāo)記-清除算法(Mark and Sweep)標(biāo)記-整理算法(Mark and Compact)復(fù)制算法(Copying)分代算法(Generational)回收過程
垃圾回收器Serial - Serial Old年輕代 Serial 垃圾回收器老年代 Serial Old 垃圾回收器
ParNew - CMS年輕代 ParNew 垃圾回收器老年代 CMS(Concurrent Mark Sweep)垃圾回收器
Parallel Scavenge - Parallel Old年輕代 Parallel Scavenge 垃圾回收器老年代 Parallel Old 垃圾回收器
G1(Garbage-First)垃圾收集器內(nèi)存結(jié)構(gòu)年輕代回收混合回收(Mixed GC)
垃圾回收器的選擇
垃圾回收算法
Java 虛擬機(jī)通過釋放不再存活的對象所占用的內(nèi)存空間實(shí)現(xiàn)垃圾回收,尋找不再存活的對象的過程使用到的就是垃圾回收算法。
垃圾回收算法是計(jì)算機(jī)科學(xué)領(lǐng)域中用于管理動(dòng)態(tài)分配的內(nèi)存的一種技術(shù)。在使用動(dòng)態(tài)內(nèi)存分配的編程語言中,如 Java、C# 等,程序員可以通過申請內(nèi)存來創(chuàng)建對象和數(shù)據(jù)結(jié)構(gòu),但是不再需要這些對象時(shí),程序員通常需要手動(dòng)釋放它們所占用的內(nèi)存。如果程序員忘記釋放這些內(nèi)存或者釋放不當(dāng),就會(huì)導(dǎo)致內(nèi)存泄漏,即分配的內(nèi)存不再使用但仍然被占用,從而浪費(fèi)系統(tǒng)資源并可能導(dǎo)致程序性能下降。
垃圾回收算法的主要目標(biāo)是自動(dòng)化管理內(nèi)存的分配和釋放,以便程序員無需手動(dòng)干預(yù)。它的基本思想是通過識(shí)別和清除不再被程序引用的對象來回收它們所占用的內(nèi)存,從而減少內(nèi)存泄漏的風(fēng)險(xiǎn)并優(yōu)化系統(tǒng)的資源利用率。
1960年 John McCarthy 發(fā)布了第一個(gè)GC 算法:標(biāo)記-清除算法。
1963年 Marvin L. Minsky 發(fā)布了復(fù)制算法。
本質(zhì)上后續(xù)所有的垃圾回收算法,都是在上述兩種算法的基礎(chǔ)上優(yōu)化而來。
垃圾回收算法的評價(jià)標(biāo)準(zhǔn)
Java 垃圾回收過程會(huì)通過單獨(dú)的 GC 線程來完成,但是不管使用哪一種 GC 算法,都會(huì)有部分階段需要停止所有的用戶線程。這個(gè)過程被稱之為 Stop The World 簡稱 STW,如果 STW 時(shí)間過長則會(huì)影響用戶的使用。 判斷 GC 算法是否優(yōu)秀,可以從三個(gè)方面來考慮:
吞吐量(Throughput)
吞吐量(Throughput)是評估垃圾回收算法性能的重要指標(biāo)之一,特別是在需要處理大量數(shù)據(jù)或高并發(fā)的應(yīng)用程序中。它通常用來衡量系統(tǒng)在執(zhí)行某項(xiàng)任務(wù)時(shí)的效率,一般用 CPU 用于執(zhí)行用戶代碼的時(shí)間與 CPU 總執(zhí)行時(shí)間的比值作為評價(jià)指標(biāo),吞吐量數(shù)值越高,垃圾回收的效率就越高。吞吐量一般可以用以下公式計(jì)算:
吞吐量 = 執(zhí)行用戶代碼時(shí)間 / (執(zhí)行用戶代碼時(shí)間 + GC 時(shí)間)
如果垃圾回收時(shí)間很少,那么吞吐量接近于 1,表示大部分時(shí)間都用于執(zhí)行用戶代碼,系統(tǒng)效率高。如果垃圾回收時(shí)間很多,吞吐量會(huì)減小。
最大暫停時(shí)間
最大暫停時(shí)間指的是所有在垃圾回收過程中的 STW 時(shí)間最大值。比如如下的圖中,黃色部分的 STW 就是最大暫停時(shí)間,顯而易見上面的圖比下面的圖擁有更少的最大暫停時(shí)間。最大暫停時(shí)間越短,用戶使用系統(tǒng)時(shí)受到的影響就越短。
堆使用效率
不同垃圾回收算法,對堆內(nèi)存的使用方式是不同的。比如標(biāo)記清除算法,可以使用完整的堆內(nèi)存。而復(fù)制算法會(huì)將堆內(nèi)存一分為二,每次只能使用一半內(nèi)存。從堆使用效率上來說,標(biāo)記清除算法要優(yōu)于復(fù)制算法。
算法權(quán)衡
上述三種評價(jià)標(biāo)準(zhǔn):堆使用效率、吞吐量,以及最大暫停時(shí)間不可兼得。一般來說,堆內(nèi)存越大,最大暫停時(shí)間就越長。
堆大小的增加通常會(huì)導(dǎo)致垃圾回收的頻率降低,因?yàn)橄到y(tǒng)有更多的空間來容納對象。然而,當(dāng)堆大小增加時(shí),單次垃圾回收的時(shí)間也可能增加,這可能會(huì)導(dǎo)致較長的最大暫停時(shí)間。
因此,為了降低最大暫停時(shí)間,可以選擇減小堆大小,但這可能會(huì)影響堆使用效率和吞吐量。
想要減少最大暫停時(shí)間,就會(huì)降低吞吐量。不同的垃圾回收算法,適用于不同的場景。
標(biāo)記-清除算法(Mark and Sweep)
這是最基本的垃圾回收算法之一。它分為兩個(gè)階段:標(biāo)記階段和清除階段。
標(biāo)記階段:從根對象開始,通過可達(dá)性分析,標(biāo)記所有被使用的對象。清除階段:清除所有未被標(biāo)記的對象,釋放它們所占用的內(nèi)存空間。這個(gè)階段可能會(huì)導(dǎo)致內(nèi)存碎片化。
優(yōu)點(diǎn):實(shí)現(xiàn)簡單,只需要在第一階段給每個(gè)對象維護(hù)標(biāo)志位,第二階段刪除對象即可。 缺點(diǎn):
碎片化問題:由于內(nèi)存是連續(xù)的,所以在對象被刪除之后,內(nèi)存中會(huì)出現(xiàn)很多細(xì)小的可用內(nèi)存單元。如果我們需要的是一個(gè)比較大的空間,很有可能這些內(nèi)存單元的大小過小無法進(jìn)行分配。 分配速度慢:由于內(nèi)存碎片的存在,Java 虛擬機(jī)需要在內(nèi)存中維護(hù)一個(gè)空閑鏈表,極有可能發(fā)生每次需要遍歷到鏈表的最后才能獲得合適的內(nèi)存空間。
標(biāo)記-整理算法(Mark and Compact)
標(biāo)記-整理算法也叫標(biāo)記壓縮算法,是對標(biāo)記-清理算法中容易產(chǎn)生內(nèi)存碎片問題的一種解決方案。核心思想分為兩個(gè)階段:
標(biāo)記階段:將所有存活的對象進(jìn)行標(biāo)記。整理階段:將存活對象移動(dòng)到堆的一端,然后直接清理掉邊界外的內(nèi)存。
優(yōu)點(diǎn):
內(nèi)存使用效率高:整個(gè)堆內(nèi)存都可以使用,不會(huì)像復(fù)制算法只能使用半個(gè)堆內(nèi)存不會(huì)發(fā)生碎片化:在整理階段可以將對象往內(nèi)存的一側(cè)進(jìn)行移動(dòng),剩下的空間都是可以分配對象的空間。
缺點(diǎn):整理階段的效率不高
復(fù)制算法(Copying)
復(fù)制算法的核心思想是:
準(zhǔn)備兩塊空間 From 空間和 To 空間,每次在對象分配階段,只能使用其中一塊空間(From 空間)。在垃圾回收階段,將 From 中存活對象復(fù)制到 To 空間。清理 From 空間,并把 From 和 To 名稱互換
優(yōu)點(diǎn):
吞吐量高:復(fù)制算法只需要遍歷一次存活對象復(fù)制到 To 空間即可,比標(biāo)記-整理算法少了一次遍歷的過程,因而性能較好,但是不如標(biāo)記-清除算法,因?yàn)闃?biāo)記-清除算法不需要進(jìn)行對象的移動(dòng)。不會(huì)發(fā)生碎片化:復(fù)制算法在復(fù)制之后就會(huì)將對象按順序放入 To 空間中,所以對象以外的區(qū)域都是可用空間,不存在碎片化內(nèi)存空間。
缺點(diǎn):
內(nèi)存使用率低:每次只能讓一半的內(nèi)存空間來為創(chuàng)建對象使用。
分代算法(Generational)
分代算法是一種應(yīng)用最廣的垃圾回收算法,分代算法認(rèn)為大多數(shù)對象的生命周期非常短暫,基于這一假設(shè),它將堆內(nèi)存劃分為不同的區(qū)域(或稱為代),并根據(jù)對象的生命周期將不同的垃圾回收算法應(yīng)用于不同的區(qū)域。
通常情況下,JVM的堆內(nèi)存被劃分為兩個(gè)主要的代:
新生代(Young Generation):新生代是年輕對象的存放區(qū)域。新生代一般被劃分為兩部分:Eden 區(qū)(伊甸園區(qū))和兩個(gè) Survivor區(qū)(通常是 From 區(qū)和 To 區(qū))。老年代(Old Generation 或 Tenured Generation):老年代主要存放長期存活的對象。
回收過程
初始時(shí),新生代一般由三部分組成:Eden 區(qū)和兩個(gè) Survivor 區(qū) S0 和 S1,分別對應(yīng) From 區(qū)和 To 區(qū)。當(dāng)應(yīng)用程序創(chuàng)建新的對象時(shí),對象會(huì)被分配到 Eden 區(qū)。隨著對象在 Eden 區(qū)越來越多,如果 Eden 區(qū)滿,新創(chuàng)建的對象已經(jīng)無法放入,就會(huì)觸發(fā)年輕代的GC,稱為 Minor GC 或者 Young GC。Minor GC 的目標(biāo)是清理 Eden 區(qū)和 From 區(qū),將仍然存活的對象移到 To 區(qū)(參考復(fù)制算法)。
垃圾收集器開始時(shí),會(huì)標(biāo)記所有在 Eden 區(qū)和 From 區(qū)中存活的對象。未被標(biāo)記的對象被視為垃圾。標(biāo)記階段結(jié)束后,垃圾收集器會(huì)將存活的對象復(fù)制到 To 區(qū),并清理 Eden 區(qū)和 From 區(qū),接下來,S0 會(huì)變成 To 區(qū),S1 變成 From 區(qū)。當(dāng) Eden 區(qū)滿時(shí)再往里放入對象,依然會(huì)發(fā)生 Minor GC 。
此時(shí)會(huì)回收 Eden 區(qū)和 S1(From)中的對象,并把 Eden 和 From 區(qū)中剩余的對象放入 S0,垃圾清理后,S0 又變成 From 區(qū),S1 又變成 To 區(qū)。
對象在 Survivor 區(qū)每經(jīng)過一次 Minor GC,其年齡會(huì)增加。當(dāng)達(dá)到一定年齡閾值(通常是15歲),對象將會(huì)被晉升到老年代。
當(dāng)老年代無法為新的大對象分配足夠的空間,或者老年代中存放的對象已經(jīng)占滿了老年代空間時(shí),會(huì)觸發(fā) Full GC 來嘗試釋放未使用的對象,以騰出空間。Full GC 會(huì)對整個(gè)堆進(jìn)行垃圾回收。如果 Full GC 依然無法回收掉老年代的對象,那么當(dāng)對象繼續(xù)放入老年代時(shí),就會(huì)拋出 Out Of Memory 異常。
使用以下JVM參數(shù),可以調(diào)整新生代中 Eden 空間與 Survivor 空間的比例:
-XX:SurvivorRatio
例如,如果設(shè)置 -XX:SurvivorRatio=8,表示每個(gè) Survivor 空間的大小是 Eden 空間大小的 1/8
垃圾回收器
垃圾回收器是垃圾回收算法的具體實(shí)現(xiàn),Java虛擬機(jī)中的垃圾回收器有多種類型,由于垃圾回收器分為年輕代和老年代,除了 G1 之外其他垃圾回收器必須成對組合進(jìn)行使用。具體的組合關(guān)系如下圖:
總結(jié)起來,目前還在流行的就是以下幾種:
Serial - Serial OldParNew - CMSParallel Scavenge - Parallel OldG1
Serial - Serial Old
使用以下虛擬機(jī)參數(shù)可以指定使用 Java 虛擬機(jī)使用 Serial 垃圾回收器組合,即同時(shí)使用 Serial 收集器(新生代)和 Serial Old 收集器(老年代):
-XX:+UseSerialGC
年輕代 Serial 垃圾回收器
Serial 垃圾回收器是 Java 虛擬機(jī)中最基本的垃圾回收器之一,主要用于新生代的垃圾回收。它的特點(diǎn)和工作原理如下:
工作方式:Serial 垃圾回收器使用單線程進(jìn)行垃圾回收操作,因此也稱為串行垃圾回收器。它通過復(fù)制算法算來管理新生代的內(nèi)存空間。Stop-the-World:在進(jìn)行垃圾回收時(shí),Serial 垃圾回收器會(huì)觸發(fā)一個(gè)停止所有應(yīng)用線程的停頓事件,這被稱為“Stop-the-World”事件。在這個(gè)時(shí)間段內(nèi),所有應(yīng)用程序的執(zhí)行都會(huì)暫停,直到垃圾回收完成。適用場景:由于其單線程執(zhí)行的特點(diǎn),Serial 垃圾回收器適合于簡單的客戶端應(yīng)用程序或者對資源要求較少的場景。它的主要優(yōu)點(diǎn)是實(shí)現(xiàn)簡單,對系統(tǒng)資源消耗較低,但也因?yàn)閱尉€程執(zhí)行的緣故,在多核處理器上不能充分利用硬件并行性能。配對關(guān)系:Serial 垃圾回收器通常與 Serial Old 垃圾回收器組合使用,形成串行收集器組合。這種組合適合于資源有限、對垃圾回收性能要求不高的場景。
老年代 Serial Old 垃圾回收器
Serial Old 是 Serial 收集器在老年代的應(yīng)用。它的特點(diǎn)和工作原理如下:
單線程執(zhí)行:和 Serial 收集器一樣,Serial Old 也是單線程執(zhí)行的。這意味著在進(jìn)行老年代的垃圾回收時(shí),只有一個(gè)線程在執(zhí)行回收操作。這種設(shè)計(jì)簡單而有效,但也限制了其在多核處理器上的并行能力。標(biāo)記-整理算法:Serial Old 使用標(biāo)記-整理(Mark-Compact)算法進(jìn)行垃圾回收。這種算法首先標(biāo)記出所有存活的對象,然后將它們向一端移動(dòng),之后清理掉未使用的內(nèi)存空間,從而整理出連續(xù)的內(nèi)存空間,為新對象的分配提供空間。適用場景:Serial Old 主要適用于簡單的客戶端應(yīng)用或者小型應(yīng)用場景,例如桌面應(yīng)用或者低流量的服務(wù)端應(yīng)用。這些應(yīng)用通常對資源消耗和垃圾回收停頓時(shí)間的要求相對較低,可以接受偶爾的較長停頓時(shí)間。配對關(guān)系:Serial Old 垃圾回收器通常與 Serial 垃圾回收器組合使用,形成串行收集器組合。這種組合適合于資源有限、對垃圾回收性能要求不高的場景。
ParNew - CMS
使用以下虛擬機(jī)參數(shù)可以指定使用 Java 虛擬機(jī)指定使用 ParNew 垃圾回收器(新生代):
-XX UseParNewGC
使用以下虛擬機(jī)參數(shù)可以指定使用 Java 虛擬機(jī)指定使用 CMS 垃圾回收器(老年代):
-XX:+UseConcMarkSweepGC
年輕代 ParNew 垃圾回收器
ParNew 垃圾回收器是用于新生代的并行垃圾回收器。它主要與 CMS 垃圾回收器結(jié)合使用,用于提高新生代的垃圾回收效率。它的特點(diǎn)和工作原理如下:
并行執(zhí)行:ParNew 垃圾回收器使用多線程并行執(zhí)行垃圾回收操作。與 Serial 垃圾回收器不同,ParNew 可以利用多核處理器的優(yōu)勢,在多個(gè)線程之間并行地執(zhí)行垃圾回收任務(wù),從而提高回收效率。與 CMS 垃圾回收器結(jié)合:ParNew 垃圾回收器通常與 CMS 垃圾回收器組合使用。CMS 負(fù)責(zé)老年代的并發(fā)標(biāo)記和清理,而 ParNew 則負(fù)責(zé)新生代的并行垃圾回收。這種組合能夠在盡量減少應(yīng)用程序停頓的同時(shí),高效地管理整個(gè)堆內(nèi)存。Stop-the-World:盡管 ParNew 使用并行執(zhí)行,但在執(zhí)行 Full GC 時(shí),仍然會(huì)觸發(fā)停頓事件(Stop-the-World)。這時(shí)所有的應(yīng)用線程都會(huì)暫停,以確保垃圾回收器能夠正確地清理整個(gè)堆內(nèi)存。性能調(diào)優(yōu):可以通過 JVM 的啟動(dòng)參數(shù)來配置 ParNew 垃圾回收器的線程數(shù)目、堆內(nèi)存大小等參數(shù),以達(dá)到最佳的性能和響應(yīng)時(shí)間。
老年代 CMS(Concurrent Mark Sweep)垃圾回收器
CMS 垃圾回收器使用并發(fā)標(biāo)記(Concurrent Marking)來避免在大部分回收過程中停止應(yīng)用程序的執(zhí)行。它的核心思想是在盡可能短的停頓時(shí)間內(nèi)完成大部分垃圾回收工作。
CMS執(zhí)行步驟:
初始標(biāo)記:用極短的時(shí)間標(biāo)記出 GC Root 能直接關(guān)聯(lián)到的對象。并發(fā)標(biāo)記:并發(fā)地標(biāo)記出所有存活的對象,用戶線程不需要暫停。重新標(biāo)記:由于并發(fā)標(biāo)記階段有些對象會(huì)發(fā)生了變化,存在錯(cuò)標(biāo)、漏標(biāo)等情況,需要重新標(biāo)記。并發(fā)清理:清理死亡的對象,用戶線程不需要暫停。
CMS垃圾回收器存在的問題:
CMS 使用了標(biāo)記-清除算法,在垃圾收集結(jié)束之后會(huì)出現(xiàn)大量的內(nèi)存碎片,CMS 會(huì)在 Full GC 時(shí)進(jìn)行碎片的整理。這樣會(huì)導(dǎo)致用戶線程暫停,以下虛擬機(jī)參數(shù)指定次數(shù)不壓縮的 Full GC 后,執(zhí)行一次帶壓縮的 Full GC。默認(rèn)值為0,表示每次進(jìn)入 Full GC 時(shí)都進(jìn)行碎片整理:-XX:CMSFullGCsBeforeCompaction=N
無法處理在并發(fā)清理過程中產(chǎn)生的浮動(dòng)垃圾,不能做到完全的垃圾回收,也就是 CMS 過程中最后一步時(shí)候產(chǎn)生的垃圾對象沒有辦法被清理。如果老年代內(nèi)存不足無法分配對象,CMS 就會(huì)退化成 Serial Old 單線程回收老年代。
Parallel Scavenge - Parallel Old
使用以下虛擬機(jī)參數(shù)任意一個(gè),都可以告訴虛擬機(jī)使用這個(gè)組合:
-XX:+UseParallelGC
-XX:+UseParallelOldGC
年輕代 Parallel Scavenge 垃圾回收器
Parallel Scavenge是 JDK8 默認(rèn)的年輕代垃圾回收器,多線程并行回收,關(guān)注的是系統(tǒng)的吞吐量。具備自動(dòng)調(diào)整堆內(nèi)存大小的特點(diǎn)。
Parallel Scavenge允許手動(dòng)設(shè)置最大暫停時(shí)間和吞吐量,垃圾回收器會(huì)通過自動(dòng)調(diào)整堆大小盡量滿足設(shè)置的最大暫停時(shí)間和吞吐量,當(dāng)堆較小時(shí),回收所需要的時(shí)間自然會(huì)縮短,從而提升了最大暫停時(shí)間和吞吐量。
Oracle 官方建議在使用這個(gè)組合時(shí),不要設(shè)置堆內(nèi)存的最大值,垃圾回收器會(huì)根據(jù)最大暫停時(shí)間和吞吐量自動(dòng)調(diào)整內(nèi)存大小。
使用以下虛擬機(jī)參數(shù)可以設(shè)置期望的最大垃圾收集停頓時(shí)間,單位毫秒:
-XX:MaxGCPauseMillis=n
使用以下虛擬機(jī)參數(shù)可以設(shè)置垃圾收集時(shí)間占總時(shí)間的比例,套入公式 1 / (1 + n) 就能得到具體的比例,假設(shè)此處 n 設(shè)置為19,則比例為 5%:
--GCTimeRatio=n
以下虛擬機(jī)參數(shù)可以讓垃圾回收器根據(jù)吞吐量和最大停頓的毫秒數(shù)自動(dòng)調(diào)整內(nèi)存大小
-XX:+UseAdaptiveSizePolicy
老年代 Parallel Old 垃圾回收器
Parallel Old 垃圾收集器與 Parallel Scavenge 垃圾收集器配合使用,可以實(shí)現(xiàn)全局并行的垃圾收集。它們都使用多線程來加速垃圾收集過程,以提高垃圾收集的吞吐量和效率。
G1(Garbage-First)垃圾收集器
G1(Garbage-First)垃圾回收器是Java虛擬機(jī)中一種現(xiàn)代化的垃圾回收器,它在 JDK 7 中首次推出,并在 JDK 9 及以后的版本中繼續(xù)得到改進(jìn)和優(yōu)化。G1 能夠同時(shí)處理新生代和老年代的垃圾回收,不像之前介紹的那些要配對使用。JDK 9 之后默認(rèn)的垃圾回收器是 G1 垃圾回收器。Parallel Scavenge 關(guān)注吞吐量,允許用戶設(shè)置最大暫停時(shí)間,但是會(huì)減少年輕代可用空間的大小。CMS 關(guān)注暫停時(shí)間,但是吞吐量方面會(huì)下降。而 G1 設(shè)計(jì)目標(biāo)就是將上述兩種垃圾回收器的優(yōu)點(diǎn)融合:
支持巨大的堆空間回收,并有較高的吞吐量。支持多 CPU 并行垃圾回收。允許用戶設(shè)置最大暫停時(shí)間。
JDK8 的較新版本和 JDK9 之后強(qiáng)烈建議使用 G1 垃圾回收器,使用以下虛擬機(jī)參數(shù)可以開啟 G1,JDK 9 以后默認(rèn)使用 G1:
-XX:+UseG1GC
內(nèi)存結(jié)構(gòu)
G1 的整個(gè)堆會(huì)被劃分成多個(gè)大小相等的區(qū)域,稱之為區(qū)(Region),區(qū)不要求是連續(xù)的。分為 Eden 、Survivor 、Old 區(qū)。Region 的大小通過(堆空間大小 / 2048)計(jì)算得到。
通過以下虛擬機(jī)參數(shù)可以指定 Region的大小,Region 大小必須是2的指數(shù)冪,取值范圍從1M 到 32M:
-XX:G1HeapRegionSize=32m
G1 垃圾回收器有兩種回收方式:
年輕代回收(Young GC) - 只處理 Eden 和 Survivor 等年輕代區(qū)域混合回收(Mixed GC)- 年輕代和老年代都會(huì)處理
年輕代回收
年輕代回收(Young GC),回收 Eden 區(qū)和 Survivor 區(qū)中不用的對象。會(huì)導(dǎo)致STW,G1 中可以通過參數(shù),通過設(shè)置每次垃圾回收時(shí)的最大暫停時(shí)間毫秒數(shù),G1 垃圾回收器會(huì)盡可能地保證暫停時(shí)間。
-XX:MaxGCPauseMillis=200
新創(chuàng)建的對象會(huì)存放在 Eden 區(qū)。當(dāng) G1 判斷年輕代區(qū)不足,無法分配對象時(shí)需要回收時(shí)會(huì)執(zhí)行Young GC。Young GC 首先會(huì)標(biāo)記出 Eden 和 Survivor 區(qū)域中的存活對象。根據(jù)配置的最大暫停時(shí)間選擇某些區(qū)域?qū)⒋婊顚ο髲?fù)制到一個(gè)新的 Survivor 區(qū)中(年齡+1),然后清空這些區(qū)域。
G1 在進(jìn)行 Young GC 的過程中會(huì)去記錄每次垃圾回收時(shí)每個(gè) Eden 區(qū)和 Survivor 區(qū)的平均耗時(shí),以作為下次回收時(shí)的參考依據(jù)。這樣就可以根據(jù)配置的最大暫停時(shí)間計(jì)算出本次回收時(shí)最多能回收多少個(gè) Region 區(qū)域了。例如,最大暫停時(shí)間200毫秒,平均每個(gè) Region 回收需要40毫秒,那么這次回收最多只能回收 4個(gè) Region。
后續(xù) Young GC 時(shí)與之前相同,只不過 Survivor 區(qū)中存活對象會(huì)被搬運(yùn)到另一個(gè) Survivor 區(qū),當(dāng)某個(gè)存活對象的年齡到達(dá)閾值(默認(rèn)15),將被放入老年代。
部分對象如果大小超過 Region 的一半,會(huì)直接放入老年代,這類老年代被稱為 Humongous 區(qū)。比如堆內(nèi)存是4G,每個(gè) Region 是2M,只要一個(gè)大對象超過了 1M 就被放入 Humongous 區(qū),如果對象過大會(huì)橫跨多個(gè)Region 。
混合回收(Mixed GC)
多次回收之后,會(huì)出現(xiàn)很多 Old 老年代區(qū),此時(shí)總堆占有率達(dá)到閾值時(shí)(默認(rèn)為 45%)會(huì)啟動(dòng) Mixed GC 回收所有年輕代和部分老年代的對象以及大對象區(qū)。采用復(fù)制算法來完成。通過以下虛擬機(jī)參數(shù)可以配置這個(gè)閾值:
-XX:InitiatingHeapOccupancyPercent=45
混合回收的基本步驟:
初始標(biāo)記階段(Initial Marking Phase):標(biāo)記 GC Root 直接引用的對象為存活。并發(fā)標(biāo)記階段(Concurrent Marking Phase):將第一步中標(biāo)記的對象引用的對象,標(biāo)記為存活。最終標(biāo)記階段(Final Marking Phase):處理在并發(fā)標(biāo)記期間應(yīng)用程序有可能更新的引用關(guān)系,確保標(biāo)記的準(zhǔn)確性。并發(fā)清理階段(Concurrent Cleanup Phase):將存活對象復(fù)制到別的 Region,由于使用復(fù)制算法,不會(huì)產(chǎn)生內(nèi)存碎片。
G1 對老年代的清理會(huì)優(yōu)先選擇存活度最低的區(qū)域(就是垃圾最多)來進(jìn)行回收,這樣可以保證回收效率最高,這也是G1(Garbage first)名稱的由來。 如果清理過程中發(fā)現(xiàn)沒有足夠的空 Region 存放轉(zhuǎn)移的對象,會(huì)出現(xiàn) Full GC 。單線程執(zhí)行標(biāo)記-整理算法,此時(shí)會(huì)導(dǎo)致用戶線程的暫停。所以盡量保證應(yīng)該用的堆內(nèi)存有一定多余的空間。
垃圾回收器的選擇
垃圾回收器的組合關(guān)系雖然很多,但是針對幾個(gè)特定的版本,比較好的組合選擇如下:
JDK8 及之前:ParNew + CMS(關(guān)注暫停時(shí)間)、Parallel Scavenge + Parallel Old (關(guān)注吞吐量) 、G1(JDK8的前期版本及JDK8前的版本不建議,因?yàn)椴惶晟戚^,適用于堆內(nèi)存較大并且關(guān)注暫停時(shí)間)JDK9 之后:G1(默認(rèn)),從 JDK9 之后,由于 G1 日趨成熟,JDK 默認(rèn)的垃圾回收器已經(jīng)修改為G1,所以強(qiáng)烈建議在生產(chǎn)環(huán)境上使用G1。
柚子快報(bào)邀請碼778899分享:java JVM(四)
相關(guān)文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。