您好,登錄后才能下訂單哦!
本篇內容主要講解“G1垃圾回收器與CMS的區別有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“G1垃圾回收器與CMS的區別有哪些”吧!
在G1之前的垃圾收集器,將堆區主要劃分了Eden區,Old區,Survivor區。其中對于Eden,Survivor對回收過程來說叫做“年輕代垃圾收集”。并且年輕代和老年代都分別是連續的內存空間。G1將堆分成了若干Region,Region的大小可以通過G1HeapRegionSize參數進行設置,其必須是2的冪,范圍允許為1Mb到32Mb。 JVM的會基于堆內存的初始值和最大值的平均數計算分區的尺寸,平均的堆尺寸會分出約2000個Region。分區大小一旦設置,則啟動之后不會再變化。
Eden regions(年輕代-Eden區)
Survivor regions(年輕代-Survivor區)
Old regions(老年代)
Humongous regions(巨型對象區域)
Free resgions(未分配區域,也會叫做可用分區)
1)G1還是采用分代回收,但是不同的分代之間內存不一定是連續的,不同分代的Region的占用數也不一定是固定的(不建議通過相關選項顯式設置年輕代大小。會覆蓋暫停時間目標)。年輕代的Eden,Survivor數量會隨著每一次GC發生相應的改變。
2)分區是不固定屬于哪個分代的,所以比如一次ygc過后,原來的Eden的分區就會變成空閑的可用分區,隨后也可能被用作分配巨型對象,成為H區等。
3)G1中的巨型對象是指,占用了Region容量的50%以上的一個對象。Humongous區,就專門用來存儲巨型對象。如果一個H區裝不下一個巨型對象,則會通過連續的若干H分區來存儲。因為巨型對象的轉移會影響GC效率,所以并發標記階段發現巨型對象不再存活時,會將其直接回收。ygc也會在某些情況下對巨型對象進行回收。
4)分區可以有效利用內存空間,因為收集整體是使用“標記-整理”,Region之間基于“復制”算法,GC后會將存活對象復制到可用分區(未分配的分區),所以不會產生空間碎片。
5)G1類似CMS,也會在比如一次fullgc中基于堆尺寸的計算重新調整(增加)堆的空間。但是相較于執行fullgc,G1 GC會在無法分配對象或者巨型對象無法獲得連續分區來分配空間時,優先嘗試擴展堆空間來獲得更多的可用分區。原則上就是G1會計算執行GC的時間,并且極力減少花在GC上的時間(包括ygc,mixgc),如果可能,會通過不斷擴展堆空間來滿足對象分配、轉移的需要。
6)因為G1提供了“可預測的暫停時間”,也是基于G1的啟發式算法,所以G1會估算年輕代需要多少分區,以及還有多少分區要被回收。younggc觸發的契機就是在Eden分區數量達到上限時。一次younggc會回收所有的Eden和survivor區。其中存活的對象會被轉移到另一個新的survivor區或者old區,如果轉移的目標分區滿了,會再將可用區標記成S或者O區。
TLAB(Thread Local Allocation Buffer)本地線程緩沖區
G1 GC會默認會啟用Tlab優化。其作用就是在并發情況下,基于CAS的獨享線程(mutator threads)可以優先將對象分配在一塊內存區域(屬于Java堆的Eden中),只是因為是Java線程獨享的內存區,沒有鎖競爭,所以分配速度更快,每個Tlab都是一個線程獨享的。如果待分配的對象被判斷是巨型對象,則不使用TLAB。
PLAB(Promotion Local Allocation Buffer) 晉升本地分配緩沖區
在younggc中,對象會將全部Eden區存活的對象轉移(復制)到S區分區。也會存在S區對象晉升(Promotion)到老年代。這個決定晉升的閥值可以通過MaxTenuringThreshold設定。晉升的過程,無論是晉升到S還是O區,都是在GC線程的PLAB中進行。每個GC線程都有一個PLAB。
Collection Sets(CSets)待收集集合
GC中待回收的region的集合。CSet中可能存放著各個分代的Region。CSet中的存活對象會在gc中被移動(復制)。GC后CSet中的region會成為可用分區。
Remembered Sets(RSets)已記憶集合
已記憶集合在每個分區中都存在,并且每個分區只有一個RSet。其中存儲著其他分區中的對象對本分區對象的引用,是一種points-in結構。ygc的時候,只要掃描RSet中的old區對象對于本young區的引用,不需要掃描所有old區。mixed gc時,掃描Old區的RSet中,其他old區對于本old分區的引用,一樣不用掃描所有的old區。提高了GC效率。因為每次GC都會掃描所有young區對象,所以RSet只有在掃描old引用young,old引用old時會被使用。
Card Table 卡表
將Java堆劃分為相等大小的一個個區域,這個小的區域(一般size在128-512字節)被當做Card,而Card Table維護著所有的Card。Card Table的結構是一個字節數組,Card Table用單字節的信息映射著一個Card。當Card中存儲了對象時,稱為這個Card被臟化了(dirty card)。 對于一些熱點Card會存放到Hot card cache。同Card Table一樣,Hot card cache也是全局的結構。
CMS收集器僅作用于老年代的收集,是基于標記-清除算法的,它的運作過程分為4個步驟:
初始標記(CMS initial mark)獨占CPU(STW),僅標記GCroots能直接關聯的對象
并發標記(CMS concurrent mark)可以和用戶線程并行執行,標記所有可達對象
重新標記(CMS remark)獨占CPU(STW),對并發標記階段用戶線程運行產生的垃圾對象進行標記修正
并發清除(CMS concurrent sweep)可以和用戶線程并行執行,清理垃圾
其中,初始標記、重新標記這兩個步驟仍然需要Stop-the-world。初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,并發標記階段就是進行GC Roots Tracing的過程,而重新標記階段則是為了修正并發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始階段稍長一些,但遠比并發標記的時間短。
并發收集、低停頓
1)對CPU非常敏感:在并發階段雖然不會導致用戶線程停頓,但是會因為占用了一部分線程使應用程序變慢
2)無法處理浮動垃圾:在最后一步并發清理過程中,用戶線程執行也會產生垃圾,但是這部分垃圾是在標記之后,所以只有等到下一次gc的時候清理掉,這部分垃圾叫浮動垃圾
3)CMS使用“標記-清理”法會產生大量的空間碎片:當碎片過多,將會給大對象空間的分配帶來問題,會出現老年代還有很大的空間但無法找到足夠大的連續空間來分配當前對象,不得不提前觸發一次FullGC,為了解決這個問題CMS提供了一個開關參數(-XX:+UseCMSCompactAtFullCollection默認開啟),用于在FullGC完成之后進行一次碎片整理,但是內存整理的過程是無法并發的,會導致停頓時間變長
年輕代垃圾回收只會回收Eden區和Survivor區。YGC時,首先G1停止應用程序的執行(Stop-The-World),G1創建回收集(Collection Set),回收集是指需要被回收的內存分段的集合,年輕代回收過程的回收集包含年輕代Eden區和Survivor區所有的內存分段。
1)第一階段,掃描根。根是指static變量指向的對象,正在執行的方法調用鏈條上的局部變量等。跟引用連同RSet記錄的外部引用作為掃描存活對象的入口。
2)第二階段,更新RSet。處理dirty card queue中的card,更新RSet。此階段完成后,RSet可以準確的反映老年代對所在的內存分段中對象的引用。
3)第三階段,處理RSet。識別被老年代對象指向的Eden中的對象,這些被指向的Eden中的對象被認為是存活的對象。
4)第四階段,復制對象。此階段,對象樹被遍歷,Eden區內存段中存活的對象會被復制到Survivor區中空的內存分段,Survivor區內存段中存活的對象如果年齡未達閾值,年齡會加1,達到閾值會被復制到Old區中空的內存分段。如果Survivor空間不夠,Eden空間的部分數據會直接晉升到老年代空間。
5)第五階段,處理引用。處理Soft,Weak,Phantom,Final,JNI Weak 等引用。最終Eden空間的數據為空,GC停止工作,而目標內存中的對象都是連續存儲的,沒有碎片,所以復制過程可以達到內存整理的效果,減少碎片。
當整個堆大小在jvm堆棧空間中占比達到IHOP閾值-XX:InitiatingHeapOccupancyPercent(默認45%)時,G1就會啟動一次混合垃圾收集周期。Mix GC不僅進行正常的新生代垃圾收集,同時也回收部分后臺掃描線程標記的老年代分區。進行Mix GC之前,會先進行全局并發標記。
1)初始標記(InitingMark):標記GC Roots,會STW,一般會復用YoungGC的暫停時間。初始標記會設置好所有分區的NTAMS值。
2)根分區掃描(RootRegionScan):根據初始標記階段確定的GC根元素,掃描這些元素所在region,獲取對老年代的引用,并標記被引用的對象。 該階段與應用線程并發執行,也就是說沒有STW停頓,必須在下一次年輕代GC開始之前完成。
3)并發標記(ConcurrentMark):遍歷整個堆,查找所有可達的存活對象。若發現區域對象中的所有對象都是垃圾,那這個區域會被立即回收。 此階段與應用線程并發執行, 也允許被年輕代GC打斷。
4)最終標記(Remark):此階段有一次STW暫停,以完成標記周期。 G1會清空SATB緩沖區,跟蹤未訪問到的存活對象,并進行引用處理。
5)清除階段(Clean UP): 這是最后的子階段,G1在執行統計和清理RSet時會有一次STW停頓。 在統計過程中,會把完全空閑的region標記出來,也會標記出適合于進行混合模式GC的候選region。 清理階段有一部分是并發執行的,比如在重置空閑region并將其加入空閑列表時。
清除階段之后,還會對存活對象進行轉移(復制算法),轉移到其他可用分區,所以當前的分區就變成了新的可用分區。復制轉移主要是為了解決分區內的碎片問題。
1)并發標記結束以后,老年代中百分百為垃圾的內存分段被回收了,部分為垃圾的內存分段被計算了出來。默認情況下,這些老年代的內存分段會分8次(可以通過-XX:G1MixedGCCountTarget設置)被回收。
2)混合回收的回收集(Collection Set)包括八分之一的老年代內存分段,Eden區內存分段,Survivor區內存分段。混合回收的算法和年輕代回收的算法完全一樣,只是回收集多了老年代的內存分段。具體過程請參考年輕代回收過程。
3)由于老年代中的內存分段默認分8次回收,G1會優先回收垃圾多的內存分段。垃圾占內存分段比例越高,越會被先回收。并且有一個閾值會決定內存分段是否被回收。-XX:G1MixedGCLiveThresholdPercent,默認為65%,意思是垃圾占內存分段比例要達到65%才會被回收。如果垃圾占比太低,意味著存活的對象占比高,在復制的時候會花費更多的時間。
4)混合回收并不一定要進行8次。有一個閾值-XX:G1HeapWastePercent,默認值為10%,意思是允許整個堆內存中有10%的空間被浪費,意味著如果發現可以回收的垃圾占堆內存的比例低于10%,則不再進行混合回收。因為GC會花費很多的時間但是回收到的內存卻很少。
1)并行與并發:G1能充分利用多CPU、多核環境下的硬件優勢,使用多個CPU來縮短Stop-the-world停頓的時間,部分其他收集器原來需要停頓Java線程執行的GC操作,G1收集器仍然可以通過并發的方式讓Java程序繼續運行。
2)分代收集
3)空間整合:與CMS的標記-清除算法不同,G1從整體來看是基于標記-整理算法實現的收集器,從局部(兩個Region之間)上來看是基于“復制”算法實現的。但無論如何,這兩種算法都意味著G1運作期間不會產生內存空間碎片,收集后能提供規整的可用內存。這種特性有利于程序長時間運行,分配大對象時不會因為無法找到連續內存空間而提前觸發下一次GC。
4)可預測的停頓:這是G1相對于CMS的另一大優勢,降低停頓時間是G1和CMS共同的關注點,但G1除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用這明確指定一個長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒。
到此,相信大家對“G1垃圾回收器與CMS的區別有哪些”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。