<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          【大內(nèi)存服務(wù)GC實(shí)踐】- 一文看懂G1GC垃圾回收器

          共 8059字,需瀏覽 17分鐘

           ·

          2022-02-25 15:56


          背景介紹


          筆者在這個(gè)系列的第一篇文章《一文看懂"ParNew+CMS"垃圾回收器》中詳細(xì)介紹了"ParNew+CMS"垃圾回收器的工作原理。文章最后筆者提到CMS垃圾回收器有兩個(gè)比較顯著的問題,一個(gè)是長時(shí)間運(yùn)行無法避免Full GC,一個(gè)是Remark階段STW時(shí)間較長。正是因?yàn)檫@兩個(gè)問題的存在,CMS垃圾回收器在JDK9被標(biāo)記棄用,慢慢開始退出歷史舞臺(tái)。有走的,就有來的,JVM重新設(shè)計(jì)了另一款垃圾回收器G1,有效地解決了CMS垃圾回收器遇到的上述兩大問題。那么,這篇文章我們就詳細(xì)探討一下G1垃圾回收器是如何解決上述兩大問題的。

          和上篇文章介紹"ParNew+CMS"垃圾回收器一樣,筆者會(huì)從G1垃圾回收器最基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)和算法出發(fā),深入分析它的工作原理,對(duì)其中的一些關(guān)鍵機(jī)制進(jìn)行剖析。

          G1核心數(shù)據(jù)結(jié)構(gòu)


          1. Region

          為什么CMS垃圾回收器長時(shí)間運(yùn)行無法避免FGC?本質(zhì)是因?yàn)镃MS垃圾回收器中老年代采用標(biāo)記清理算法,這種算法會(huì)產(chǎn)生較多內(nèi)存碎片,當(dāng)內(nèi)存碎片很碎無法給對(duì)象分配出連續(xù)空間的時(shí)候,JVM就會(huì)觸發(fā)FGC整理老年代。那有些同學(xué)就會(huì)問為什么不在每次老年代GC的時(shí)候執(zhí)行整理操作呢?這主要是因?yàn)槔夏甏绻艽蟮脑?,整理一次STW的時(shí)間會(huì)不可控。那怎么破這個(gè)局呢?JVM設(shè)計(jì)者提出了一個(gè)思路:老年代不是很大嗎,那就將老年代劃分成很多小的格子,在此基礎(chǔ)上GC算法基本不變,還是先標(biāo)記,不過在標(biāo)記完成后,按照優(yōu)先級(jí)選擇部分格子使用復(fù)制算法進(jìn)行整理,這樣每次整理的內(nèi)存空間是可控的,STW的時(shí)間就相對(duì)可控,而且因?yàn)槭褂脧?fù)制算法就不再會(huì)出現(xiàn)內(nèi)存碎片。對(duì),這就是G1垃圾回收器的核心設(shè)計(jì)思想。

          G1 是一個(gè)分代回收器,它依然保留了"ParNew+CMS"垃圾回收器中新生代和老年代的分代邏輯。但與"ParNew+CMS"回收器不同的是,它引入了一個(gè)新的數(shù)據(jù)結(jié)構(gòu) - Region,也就是上文中筆者提到的小格子。G1垃圾回收器會(huì)把整個(gè)堆劃分成一個(gè)個(gè)大小相等的Region,每個(gè)Region的大小可以通過參數(shù)設(shè)置,通常大小在1~32M之間,默認(rèn)大小取決于總堆的大小。下圖是G1回收器堆劃分的示意圖:


          上圖中整個(gè)堆區(qū)會(huì)被劃分為新生代以及老年代,其中新生代又分為Eden區(qū)和Survivor區(qū),這和"ParNew+CMS"回收器是相同的。不同的主要有兩點(diǎn):
          (1)G1回收器不要求新生代或者老年代的Region連續(xù)分布,換言之,這些Region可以隨意分布在整個(gè)堆區(qū),它和新生代或者老年代的隸屬關(guān)系屬于邏輯隸屬。
          (2)如果一個(gè)對(duì)象大小超過Region大小的一半,就會(huì)被視為巨型對(duì)象(Humongous Object),巨型對(duì)象會(huì)被分配到Humongous Region。


          2. RememberedSet(RSet)和Card Table

          《一文看懂"ParNew+CMS"垃圾回收器》文章在介紹對(duì)象標(biāo)記的時(shí)候重點(diǎn)說明了一個(gè)對(duì)象跨代引用的問題。比如YGC階段標(biāo)記新生代對(duì)象的時(shí)候,不僅需要掃描GC Roots標(biāo)記其直接引用的新生代對(duì)象,還需要掃描所有老年代對(duì)象,查看其是否引用了新生代對(duì)象,如果引用了的話,對(duì)應(yīng)新生代對(duì)象也是需要標(biāo)記的,否則就會(huì)發(fā)生漏標(biāo)。然而實(shí)際實(shí)現(xiàn)中,掃描整個(gè)老年代對(duì)象的代價(jià)相當(dāng)大。文章中我們介紹了可以通過Card Table這種數(shù)據(jù)結(jié)構(gòu)大幅降低掃描老年代對(duì)象查找跨代引用的代價(jià),簡單來說,就是通過寫屏障技術(shù)在老年代對(duì)象引用新生代對(duì)象的時(shí)候?qū)ard Table對(duì)應(yīng)Card設(shè)置成"臟卡",這樣的話,查找跨代引用,只需要掃描Card Table臟卡對(duì)應(yīng)的老年代區(qū)域,而不需要掃描整個(gè)老年代,大大降低了掃描代價(jià)。

          根據(jù)上文介紹,G1也是一個(gè)分代回收器,也需要處理跨代引用掃描代價(jià)大的問題。為了解決這個(gè)問題,G1引用了另一種新的數(shù)據(jù)結(jié)構(gòu) - Remembered Set,簡稱RSet。

          2.1 RSet是什么樣的數(shù)據(jù)結(jié)構(gòu)呢?

          在G1實(shí)現(xiàn)中,每個(gè)Region會(huì)被劃分成很多512Byte大小的小塊,這樣一個(gè)32M的Region就會(huì)被劃分為65536個(gè)小塊。與之大小對(duì)應(yīng)的會(huì)有一個(gè)長度為65536的Card Table數(shù)組,數(shù)組的每個(gè)元素只有1Byte,對(duì)應(yīng)Region的每個(gè)小塊。Card Table數(shù)組這個(gè)結(jié)構(gòu)和CMS垃圾回收器中Card Table是相同的。

          除了Card Table數(shù)組之外,每個(gè)Region還會(huì)有一個(gè)RSet數(shù)據(jù)結(jié)構(gòu),RSet主要用來記錄哪個(gè)Region的哪個(gè)Card上的對(duì)象引用了本Region中的對(duì)象。實(shí)際實(shí)現(xiàn)中,RSet默認(rèn)是一個(gè)HashMap,Map的key是引用Region,value是一個(gè)List,List中存儲(chǔ)引用Region中的引用Card列表。Region、Card Table以及RSet的示意圖如下所示:


          上圖中,RegionA和RegionB中分別有對(duì)象引用RegionC中的對(duì)象,在RegionC對(duì)應(yīng)的RSet就會(huì)記錄這樣的引用關(guān)系。該RSet中有兩個(gè)KV對(duì),第一個(gè)KV對(duì)的key是RegionA,value是一個(gè)列表,列表中有兩個(gè)元素3和65534,分別代表RegionA中引用對(duì)象在對(duì)應(yīng)Card Table中的下標(biāo)。第二個(gè)KV對(duì)的key是RegionB,value中列表只有一個(gè)元素1565,代表RegionB中引用對(duì)象在對(duì)應(yīng)Card Table中的下標(biāo)。

          介紹到這里,大家應(yīng)該對(duì)RSet的結(jié)構(gòu)有了初步的了解。與CMS回收器中Card Table相比,Card Table中的臟卡代表"我引用了別的對(duì)象",屬于一種points-out結(jié)構(gòu)。而RSet記錄的是哪些其他Region引用了本Region中的對(duì)象,屬于points-into結(jié)構(gòu)。兩者是有本質(zhì)區(qū)別的。

          按照上述介紹,如果一個(gè)Region是熱點(diǎn)Region,表示這個(gè)Region中的對(duì)象被大量其他Region中的對(duì)象所引用,那么RSet占用的內(nèi)存空間開銷就會(huì)越大。但我們要清楚一點(diǎn),RSet內(nèi)存開銷不是為業(yè)務(wù)服務(wù)的,實(shí)際上不應(yīng)該占用太多。因此為了控制RSet占用內(nèi)存空間的大小,RSet會(huì)根據(jù)引用Region個(gè)數(shù)的多少,設(shè)置3種不同的實(shí)現(xiàn)方式(上文中介紹的是其中一種),分別稱為:
          • sparse per-region-table (PRT),從字面意思來看表示這個(gè)RSet是一個(gè)稀疏的集合。具體實(shí)現(xiàn)使用HashMap方式記錄引用關(guān)系,其中Map的key是引用Region,value是一個(gè)List,List中存儲(chǔ)引用Region中的引用Card列表。上文有過介紹。
          • fine-grained PRT,還是使用HashMap方式記錄引用關(guān)系,其中Map的key是引用Region,但value不再是List,而是一個(gè)bitmap,bit位為1表示對(duì)應(yīng)Card是引用Card,否則不是引用Card。
          • coarse-grained bitmap,從字面意思可以看出來這就是一個(gè)bitmap,不過bitmap中每個(gè)bit位引用粒度不再是Card,而是Region。如果bit位值為1,表示這個(gè)Region是引用Region,即這個(gè)Region中有對(duì)象引用了該Region中的對(duì)象。

          很顯然,上述3種實(shí)現(xiàn)方式中,spase PRT和fine-grained PRT都是精確到Card,而coarse-grained bitmap是精確到Region。


          2.2 G1是如何管理RSet的?
          G1會(huì)創(chuàng)建一個(gè)用于管理RSet的線程池,這些線程稱為Refine線程。G1中RSet的更新不是同步完成的,G1會(huì)把所有引用關(guān)系都先放入一個(gè)隊(duì)列中,稱為Dirty Card Queue(DCQ),然后使用Refine線程來消費(fèi)這個(gè)隊(duì)列完成引用關(guān)系的記錄并更新RSet。如果Refine線程忙不過來,GC線程以及應(yīng)用線程也可能會(huì)協(xié)助更新RSet。


          G1核心工作流程


          在介紹G1垃圾回收器工作流程之前,先簡單回顧一下"ParNew+CMS"回收器的工作流程,具體流程不再贅述,參考下圖:


          G1回收器核心工作流程與"ParNew+CMS"回收器基本相同,參考上圖看下圖:


          對(duì)比"ParNew+CMS"回收器工作流程,G1中同樣會(huì)比較頻繁地進(jìn)行新生代YGC,同樣也有老年代并發(fā)標(biāo)記周期。不同的是,G1在并發(fā)標(biāo)記之后并沒有直接清理全部垃圾對(duì)象,而是新增了一個(gè)混合收集周期,這個(gè)周期包含多次Mixed GC,每次Mixed GC只會(huì)回收部分Region,直至未處理Region集合占比低于特定閾值。之所以將這個(gè)周期稱為"Mixed Collection Cycle",是因?yàn)槊看蜯ixed GC都會(huì)同時(shí)回收新生代和老生代中的Region。

          上圖中,IHOP(InitiatingHeapOccupancyPercent) Trigger代表"并發(fā)標(biāo)記周期"觸發(fā)的閾值。一旦當(dāng)前JVM已使用內(nèi)存/總堆內(nèi)存超過這個(gè)閾值,就會(huì)觸發(fā)并發(fā)標(biāo)記。

          雖然G1回收器的工作流程和"ParNew+CMS"回收器差的不是很多,但是每個(gè)核心步驟的具體實(shí)現(xiàn)卻有很大的不同。接下來分別來看G1中的YGC、Concurrent Marking Cycle以及Mixed GC的一些核心實(shí)現(xiàn)細(xì)節(jié)。


          1. Young GC核心流程

          和"ParNew+CMS"一樣,一旦Eden區(qū)滿了之后,就會(huì)觸發(fā)YGC。YGC只負(fù)載回收堆中新生代的所有Region,不回收老年代的Region?;玖鞒炭梢员硎緸槿缦聨撞剑?/span>
          (1)將eden區(qū)和survivor區(qū)所有Region添加到CSet中準(zhǔn)備回收。注:CSet是一個(gè)存放待回收Region的數(shù)據(jù)結(jié)構(gòu)。
          (2)標(biāo)記階段
          • 從GC Roots開始標(biāo)記直接引用的新生代對(duì)象。
          • 基于RSet標(biāo)記跨代引用的新生代對(duì)象。
          (3)復(fù)制階段:將標(biāo)記活躍的對(duì)象復(fù)制到其他Region中。

          為了更加清楚的明白上述步驟,下圖是一次真實(shí)的YGC日志片段,可以對(duì)照著上述流程來看。


          G1在日志的細(xì)節(jié)上比"ParNew+CMS"要詳細(xì)很多,如果研究G1的話,日志是一個(gè)非常不錯(cuò)的研究入口。


          2. Concurrent Marking Cycle核心流程

          根據(jù)上文介紹,一旦"JVM已使用內(nèi)存/總內(nèi)存"的比例超過設(shè)定閾值IHOP(InitiatingHeapOccupancyPercent)后,G1會(huì)執(zhí)行一次Concurrent Marking Cycle,并在之后進(jìn)行多輪Mixed GC。和CMS回收器基本一樣,G1中一輪并發(fā)標(biāo)記周期包含初始標(biāo)記、并發(fā)標(biāo)記、重新標(biāo)記,再加上一個(gè)cleanup階,這樣就可以完成整堆所有Region的對(duì)象標(biāo)記。下面是一段G1回收器中并發(fā)標(biāo)記周期的完整日志:


          • 初始標(biāo)記(Initial Marking):初始化標(biāo)記是伴隨一次普通的YGC發(fā)生的,從GC Root開始標(biāo)記直接可達(dá)的對(duì)象。
          • 并發(fā)標(biāo)記( Concurrent Marking):這個(gè)階段標(biāo)記線程和應(yīng)用線程并發(fā)工作,遍歷整堆所有可達(dá)對(duì)象并標(biāo)記。這個(gè)階段需要特別關(guān)注并發(fā)標(biāo)記可能產(chǎn)生的"漏標(biāo)"問題,G1使用Snapshot AT Begining(簡稱SATB)算法避免漏標(biāo)問題發(fā)生,這和CMS完全不同。3.4小節(jié)深入介紹。
          • 重新標(biāo)記( Remark) :標(biāo)記那些在并發(fā)標(biāo)記階段發(fā)生變化的對(duì)象,將被回收。
          • 清理( Cleanup ):釋放沒有存活對(duì)象的Region。


            3.?Mixed GC核心流程

            一輪并發(fā)標(biāo)記之后緊接著是混合回收周期,包括多次Mixed GC,每次Mixed GC只回收部分Region。這里有三個(gè)問題:
            (1)為什么這些Region需要分為多次回收?
            主要原因是所有Region一起回收的話有可能會(huì)導(dǎo)致暫停時(shí)間比較長,尤其在內(nèi)存較大的情況下。為了每次回收的暫停時(shí)間可控,就將一次大回收分成很多次小回收。
            (2)每次回收Region集合的選擇原則是什么?
            在執(zhí)行混合回收之前,G1會(huì)將所有Region按照每個(gè)Region中垃圾對(duì)象占比進(jìn)行一次排序,垃圾對(duì)象占比越高,排名越靠前。排好序之后,每次Mixed GC按順序選擇部分Region進(jìn)行回收,選擇多少Region取決于這些Region的回收預(yù)估暫停時(shí)間不超過設(shè)置的最大暫停時(shí)間。這個(gè)回收算法稱為Garbage First,也就是G1的意思。
            (3)每次Mixed GC的過程是怎么樣的?
            具體的回收過程和上文中介紹的Young GC沒有太大區(qū)別,可以參考上文介紹。


            4. 并發(fā)標(biāo)記階段對(duì)象"漏標(biāo)"問題解法 - SATB算法

            在介紹SATB算法之前,先簡單介紹一下并發(fā)標(biāo)記過程中可能出現(xiàn)的對(duì)象"漏標(biāo)"問題。

            4.1 對(duì)象漏標(biāo)簡介
            什么是對(duì)象漏標(biāo)?就是本應(yīng)該被標(biāo)記為活躍的對(duì)象因?yàn)槟承┰蜃罱K沒有被標(biāo)記,被垃圾回收器認(rèn)為是垃圾對(duì)象而回收掉,最終導(dǎo)致應(yīng)用錯(cuò)誤,很顯然這種情況是不允許發(fā)生的。

            并發(fā)標(biāo)記階段什么場景下會(huì)發(fā)生對(duì)象漏標(biāo)?應(yīng)用線程和標(biāo)記線程并發(fā)執(zhí)行,在下面兩種場景下會(huì)出現(xiàn)漏標(biāo):
            1. 應(yīng)用線程在并發(fā)標(biāo)記過程中新生成的活躍對(duì)象因?yàn)槟承┰驔]有被標(biāo)記線程標(biāo)記。
            2. 應(yīng)用線程在并發(fā)標(biāo)記過程中變更引用關(guān)系的時(shí)候在特定場景下會(huì)出現(xiàn)漏標(biāo),具體場景涉及標(biāo)記算法"三色標(biāo)記法",詳細(xì)可以閱讀文章《【大內(nèi)存服務(wù)GC實(shí)踐】- 一文看懂”ParNew+CMS”垃圾回收器》。這里只簡單進(jìn)行描述:


            如上圖所示,老年代中有三個(gè)對(duì)象A、B和C,在標(biāo)記之前的引用關(guān)系是A引用B,B引用C。先分別介紹一下他們的標(biāo)記情況:
            • 對(duì)象A已經(jīng)被標(biāo)記為黑色,表示為A為活躍對(duì)象且所有它引用的對(duì)象也完成標(biāo)記。
            • 對(duì)象B被標(biāo)記為灰色,表示B對(duì)象是活躍對(duì)象,但是它關(guān)聯(lián)的對(duì)象還沒有被完全標(biāo)記完。
            • 對(duì)象C是白色,表示還沒有被標(biāo)記。

            在這種背景下,應(yīng)用線程此時(shí)發(fā)生了一次引用關(guān)系變更,B引用C的關(guān)系被刪除了且同時(shí)A引用了C,即如下所示代碼:
            objB.fieldC?=?null;objA.filedC = C;

            此時(shí),標(biāo)記線程就不再會(huì)標(biāo)記對(duì)象C,因?yàn)閷?duì)象A已經(jīng)是黑色,表示所有它引用的對(duì)象都已經(jīng)完成標(biāo)記。然而實(shí)際上活躍的對(duì)象C就會(huì)被漏標(biāo)最終被回收掉。

            4.2 SATB算法思想簡介
            對(duì)象漏標(biāo)介紹之后簡單介紹一下SATB算法的基本思想,可以概括為如下三句話:
            1. 并發(fā)標(biāo)記之前先給Region內(nèi)存打個(gè)快照,標(biāo)記線程基于這個(gè)快照獨(dú)立進(jìn)行標(biāo)記。應(yīng)用線程不會(huì)直接修改這個(gè)快照中的對(duì)象,也就是說應(yīng)用線程不會(huì)干擾標(biāo)記線程的工作。
            2. 應(yīng)用線程新分配的對(duì)象都認(rèn)為是活躍對(duì)象,實(shí)際在下一個(gè)并發(fā)標(biāo)記周期進(jìn)行標(biāo)記。上文說過漏標(biāo)發(fā)生的第一種場景是"應(yīng)用線程在并發(fā)標(biāo)記過程中新生成的活躍對(duì)象因?yàn)槟承┰驔]有被標(biāo)記線程標(biāo)記",那如果能夠?qū)?biāo)記階段新分配的對(duì)象全都集合到一起,這些對(duì)象全部都標(biāo)記為活躍對(duì)象(實(shí)際肯定會(huì)有部分垃圾對(duì)象,將垃圾對(duì)象標(biāo)記為活躍對(duì)象不影響程序正確性)就可以解決這個(gè)問題。
            3. 并發(fā)標(biāo)記過程中已存在對(duì)象的引用關(guān)系變更在Remark階段單獨(dú)進(jìn)行處理。上文介紹了漏標(biāo)發(fā)生的第二種場景,為了解決這個(gè)場景引入的漏標(biāo)問題,可以將引用關(guān)系變更分解為舊的引用關(guān)系先刪除,新的引用關(guān)系生成兩個(gè)步驟,只要破壞任何一個(gè)步驟就可以防止漏標(biāo)發(fā)生。因此有兩種針對(duì)性解法:
              • 在并發(fā)標(biāo)記階段如果有新引用關(guān)系生成,就記錄下來,Remark階段進(jìn)行重標(biāo)記,這個(gè)破壞了步驟二,即黑色對(duì)象重新引用了白色對(duì)象,就記錄下來重新掃描黑色對(duì)象,將其引用的所有對(duì)象都標(biāo)記成存活對(duì)象。這個(gè)就是CMS垃圾回收器使用的增量更新算法。
              • 在并發(fā)標(biāo)記階段如果有引用關(guān)系被刪除,就記錄下來,Remark階段對(duì)這些引用關(guān)系被刪除的重標(biāo)記,這個(gè)破壞了步驟一,即灰色對(duì)象斷開了白色對(duì)象引用的時(shí)候,記錄下來,后面重新把這個(gè)白色對(duì)象標(biāo)記成存活對(duì)象。這個(gè)就是G1垃圾回收器使用的算法。

            簡單來說,SATB明確將并發(fā)標(biāo)記這一個(gè)大工程分成了三個(gè)字模塊,分別是對(duì)快照進(jìn)行并發(fā)標(biāo)記、對(duì)并發(fā)標(biāo)記過程中新分配的對(duì)象全部標(biāo)記為活躍、對(duì)并發(fā)標(biāo)記過程中引用關(guān)系變更的對(duì)象單獨(dú)進(jìn)行處理。

            4.3 SATB算法實(shí)現(xiàn)
            了解了SATB算法的核心思想之后,再來看看這個(gè)算法是如何實(shí)現(xiàn)的。G1回收器將堆內(nèi)存分成一個(gè)一個(gè)Region,在Region中分配對(duì)象時(shí),對(duì)象都是連續(xù)分配的。這里介紹兩個(gè)指針:Bottom指針和Top指針,其中Bottom指針指向Region的初始位置,Top指針指向下一個(gè)對(duì)象分配的內(nèi)存位置,如果有新的對(duì)象分配,就將Top指針向前移動(dòng)。如下圖所示:


            G1使用的SATB算法是基于內(nèi)存快照的,那SATB算法具體怎么實(shí)現(xiàn)基于內(nèi)存快照的標(biāo)記呢?現(xiàn)在假設(shè)在標(biāo)記之前Region如下:


            這個(gè)時(shí)候要進(jìn)行一輪完整的并發(fā)標(biāo)記周期,按照上面的說法是要先給這個(gè)Region打個(gè)快照,這個(gè)快照實(shí)際上就是[Bottom, Top)現(xiàn)在這塊內(nèi)存區(qū)域。但是在并發(fā)標(biāo)記周期內(nèi),因?yàn)橛幸镁€程在分配對(duì)象,所以Top指針肯定會(huì)往前移動(dòng),所以為了將標(biāo)記開始前Top這個(gè)位置記錄下來,需要定義另一個(gè)指針TAMS(全稱Top-At-Mart-Start)指向標(biāo)記前Top這個(gè)位置,從Top-At-Mark-Start這個(gè)字面含義就可以理解是標(biāo)記開始時(shí)Top指針?biāo)谖恢?,這樣快照所代表的內(nèi)存區(qū)域就是[Bottom, TAMS)這塊,并發(fā)標(biāo)記過程中標(biāo)記線程就基于這塊內(nèi)存對(duì)象進(jìn)行標(biāo)記,后面Top指針就可以隨意往前移動(dòng)了。所以按照正常的邏輯應(yīng)該是這樣:


            上圖中Initial Marking剛開始的時(shí)候,Top指針和TAMS指針指向同一個(gè)內(nèi)存位置,[Bottom, TAMS)這塊內(nèi)存區(qū)域有一個(gè)對(duì)應(yīng)的bitmap,bitmap中每一位代表對(duì)應(yīng)內(nèi)存區(qū)域?qū)ο笫欠翊婊睢=?jīng)過并發(fā)標(biāo)記之后,Remark開始的時(shí)候,Top指針因?yàn)閼?yīng)用線程有分配對(duì)象所以會(huì)向前移動(dòng),并發(fā)標(biāo)記線程獨(dú)立標(biāo)記[Bottom, TAMS)這塊內(nèi)存區(qū)域?qū)?yīng)的對(duì)象,標(biāo)記后的結(jié)果使用bitmap表示(其中黑色方塊表示對(duì)應(yīng)對(duì)象被標(biāo)記為活躍對(duì)象)。
            在并發(fā)標(biāo)記過程中新生成的對(duì)象都分配在[TAMS, Top) 這塊內(nèi)存區(qū)域,G1算法會(huì)將這部分新生成的對(duì)象都認(rèn)為是存活對(duì)象,這輪標(biāo)記不處理這部分新生成對(duì)象,留到下一輪標(biāo)記處理。

            現(xiàn)在繼續(xù)來看SATB算法如何處理并發(fā)標(biāo)記過程中引用關(guān)系變更問題。在并發(fā)標(biāo)記階段,引用變更發(fā)生后通過寫屏障會(huì)將這些變更記錄并保存在一個(gè)隊(duì)列里(satb_mark_queue),在remark階段會(huì)掃描這個(gè)隊(duì)列,通過這種方式,舊的引用所指向的對(duì)象就會(huì)被標(biāo)記上,其子孫也會(huì)被遞歸標(biāo)記上,這樣就不會(huì)漏標(biāo)記任何對(duì)象,snapshot的完整性也就得到了保證。

            實(shí)際上介紹到這里基本上已經(jīng)將SATB算法實(shí)現(xiàn)介紹的比較清楚了。下圖是完整的兩輪并發(fā)標(biāo)記示意圖(摘自網(wǎng)上):


            4.4 SATB算法 vs Incremental Update算法
            G1的SATB算法在Remark階段不需要暫停遍歷整堆對(duì)象,所以避免了這個(gè)階段可能的長耗時(shí)。但是CMS垃圾回收器中增量更新算法因?yàn)闊o法知道哪些對(duì)象是并發(fā)標(biāo)記階段新增的,所以在Remark階段需要重新掃描GC Roots標(biāo)記整堆對(duì)象,這就可能帶來不可控的長耗時(shí)暫停。

            全文總結(jié)


            至此,筆者基本將G1GC的核心內(nèi)容介紹完了??梢钥吹?,G1相比CMS在很多地方都做了非常大的改動(dòng),整體思路還是比較清晰的。但是在具體實(shí)踐中,因?yàn)镚1的這種復(fù)雜性,導(dǎo)致想要用好G1,需要開發(fā)同學(xué)對(duì)其中各個(gè)參數(shù)的含義比較了解,并且要有一定的調(diào)優(yōu)經(jīng)驗(yàn)。尤其在一些大內(nèi)存場景下,一旦參數(shù)調(diào)不好,很可能GC效果會(huì)非常差。因?yàn)楣P者目前接觸到的大數(shù)據(jù)系統(tǒng)都是大內(nèi)存場景,所以在這些場景下用好G1,實(shí)際上還是需要不斷地測試和調(diào)優(yōu)。

            瀏覽 156
            點(diǎn)贊
            評(píng)論
            收藏
            分享

            手機(jī)掃一掃分享

            分享
            舉報(bào)
            評(píng)論
            圖片
            表情
            推薦
            點(diǎn)贊
            評(píng)論
            收藏
            分享

            手機(jī)掃一掃分享

            分享
            舉報(bào)
            <kbd id="afajh"><form id="afajh"></form></kbd>
            <strong id="afajh"><dl id="afajh"></dl></strong>
              <del id="afajh"><form id="afajh"></form></del>
                  1. <th id="afajh"><progress id="afajh"></progress></th>
                    <b id="afajh"><abbr id="afajh"></abbr></b>
                    <th id="afajh"><progress id="afajh"></progress></th>
                    五楼自拍 | 无码免费在线视频 | 麻豆国产精品无码人妻无码 | 日韩伦理一区二区三区 | 日日摸日日搞 |