日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

垃圾回收器CMS和G1

發布時間:2023/12/20 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 垃圾回收器CMS和G1 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • CMS與三色標記算法
        • CMS收集器
        • 三色標記算法(重點)
        • 結語
    • G1收集器
        • G1的堆內存算法
        • G1回收流程
        • G1的GC模式

CMS與三色標記算法

CMS(Concurrent Mark Sweep)是一款里程碑式的垃圾收集器,為什么這么說呢?因為在它之前,GC線程和用戶線程是無法同時工作的,即使是Parallel Scavenge,也不過是GC時開啟多個線程并行回收而已,GC的整個過程依然要暫停用戶線程,即Stop The World。這帶來的后果就是Java程序運行一段時間就會卡頓一會,降低應用的響應速度,這對于運行在服務端的程序是不能被接收的。

GC時為什么要暫停用戶線程?
首先,如果不暫停用戶線程,就意味著期間會不斷有垃圾產生,永遠也清理不干凈。
其次,用戶線程的運行必然會導致對象的引用關系發生改變,這就會導致兩種情況:漏標和錯標

漏標
原本不是垃圾,但是GC的過程中,用戶線程將其引用關系修改,導致GC Roots不可達,成為了垃圾。這種情況還好一點,無非就是產生了一些浮動垃圾,下次GC再清理就好了。
錯標
原本是垃圾,但是GC的過程中,用戶線程將引用重新指向了它,這時如果GC一旦將其回收,將會導致程序運行錯誤。
針對這些問題,CMS是如何解決的呢?它是如何做到GC線程和用戶線程并發工作的呢?

CMS收集器

CMS收集器是?種以獲取最短回收停頓時間為?標的收集器。它??常符合在注重?戶體驗的應?上使?。
CMS收集器是HotSpot虛擬機第?款真正意義上的并發收集器,它第?次實現了讓垃圾收集線程與?戶線程(基本上)同時?作

Concurrent Mark Sweep,從名字上就可以看出來,這是一款采用「標記清除」算法的垃圾收集器,它運行的示意圖大概如下:
大概可分為四個主要步驟

1、初始標記
初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快。初始標記的過程是需要觸發STW的,不過這個過程非常快,而且初試標記的耗時不會因為堆空間的變大而變慢,是可控的,因此可以忽略這個過程導致的短暫停頓。

2、并發標記
并發標記就是將初始標記的對象進行深度遍歷,以這些對象為根,遍歷整個對象圖,這個過程耗時較長,而且標記的時間會隨著堆空間的變大而變長。不過好在這個過程是不會觸發STW的,用戶線程仍然可以工作,程序依然可以響應,只是程序的性能會受到一點影響。因為GC線程會占用一定的CPU和系統資源,對處理器比較敏感。CMS默認開啟的GC線程數是:(CPU核心數+3)/4,當CPU核心數超過4個時,GC線程會占用不到25%的CPU資源,如果CPU數不足4個,GC線程對程序的影響就會非常大,導致程序的性能大幅降低。

3、重新標記
由于并發標記時,用戶線程仍在運行,這意味著并發標記期間,用戶線程有可能改變了對象間的引用關系,可能會發生兩種情況:一種是原本不能被回收的對象,現在可以被回收了,另一種是原本可以被回收的對象,現在不能被回收了。針對這兩種情況,CMS需要暫停用戶線程,進行一次重新標記。

4、并發清理
重新標記完成后,就可以并發清理了。這個過程耗時也比較長,且清理的開銷會隨著堆空間的變大而變大。不過好在這個過程也是不需要STW的,用戶線程依然可以正常運行,程序不會卡頓,不過和并發標記一樣,清理時GC線程依然要占用一定的CPU和系統資源,會導致程序的性能降低。

CMS收集器的優缺點:
盡管CMS是一款里程碑式的垃圾收集器,開啟了GC線程和用戶線程同時工作的先河,但是不管是哪個JDK版本,CMS從來都不是默認的垃圾收集器,究其原因,還是因為CMS不太完美,存在一些缺點。
1、對處理器敏感
并發標記、并發清理階段,雖然CMS不會觸發STW,但是標記和清理需要GC線程介入處理,GC線程會占用一定的CPU資源,進而導致程序的性能下降,程序響應速度變慢。CPU核心數多的話還稍微好一點,CPU資源緊張的情況下,GC線程對程序的性能影響非常大。

2、浮動垃圾
并發清理階段,由于用戶線程仍在運行,在此期間用戶線程制造的垃圾就被稱為“浮動垃圾”,浮動垃圾本次GC無法清理,只能留到下次GC時再清理。

3、并發失敗
由于浮動垃圾的存在,因此CMS必須預留一部分空間來裝載這些新產生的垃圾。CMS不能像Serial Old收集器那樣,等到Old區填滿了再來清理。在JDK5時,CMS會在老年代使用了68%的空間時激活,預留了32%的空間來裝載浮動垃圾,這是一個比較偏保守的配置。如果實際引用中,老年代增長的不是太快,可以通過-XX:CMSInitiatingOccupancyFraction參數適當調高這個值。到了JDK6,觸發的閾值就被提升至92%,只預留了8%的空間來裝載浮動垃圾。
如果CMS預留的內存無法容納浮動垃圾,那么就會導致「并發失敗」,這時JVM不得不觸發預備方案,啟用Serial Old收集器來回收Old區,這時停頓時間就變得更長了

4、內存碎片
由于CMS采用的是「標記清除」算法,這就意味這清理完成后會在堆中產生大量的內存碎片。內存碎片過多會帶來很多麻煩,其一就是很難為大對象分配內存。導致的后果就是:堆空間明明還有很多,但就是找不到一塊連續的內存區域為大對象分配內存,而不得不觸發一次Full GC,這樣GC的停頓時間又會變得更長。
針對這種情況,CMS提供了一種備選方案,通過-XX:CMSFullGCsBeforeCompaction參數設置,當CMS由于內存碎片導致觸發了N次Full GC后,下次進入Full GC前先整理內存碎片,不過這個參數在JDK9被棄用了。

三色標記算法(重點)

介紹完CMS垃圾收集器后,我們有必要了解一下,為什么CMS的GC線程可以和用戶線程一起工作

JVM判斷對象是否可以被回收,絕大多數采用的都是「可達性分析」算法,關于這個算法,可以查看筆者以前的文章:大白話理解可達性分析算法。

從GC Roots開始遍歷,可達的就是存活,不可達的就回收。
CMS將對象標記為三種顏色:

標記的過程大致如下:

  • 剛開始,所有的對象都是白色,沒有被訪問。
  • 將GC Roots直接關聯的對象置為灰色。
  • 遍歷灰色對象的所有引用,灰色對象本身置為黑色,引用置為灰色。
  • 重復步驟3,直到沒有灰色對象為止。
  • 結束時,黑色對象存活,白色對象回收。
  • 這個過程正確執行的前提是沒有其他線程改變對象間的引用關系,然而,并發標記的過程中,用戶線程仍在運行,因此就會產生漏標和錯標的情況。

    漏標
    假設GC已經在遍歷對象B了,而此時用戶線程執行了A.B=null的操作,切斷了A到B的引用。

    本來執行了A.B=null之后,B、D、E都可以被回收了,但是由于B已經變為灰色,它仍會被當做存活對象,繼續遍歷下去。
    最終的結果就是本輪GC不會回收B、D、E,留到下次GC時回收,也算是浮動垃圾的一部分。

    實際上,這個問題依然可以通過「寫屏障」來解決,只要在A寫B的時候加入寫屏障,記錄下B被切斷的記錄,重新標記時可以再把他們標為白色即可。

    錯標
    假設GC線程已經遍歷到B了,此時用戶線程執行了以下操作:

    B.D=null;//B到D的引用被切斷 A.xx=D;//A到D的引用被建立


    B到D的引用被切斷,且A到D的引用被建立。
    此時GC線程繼續工作,由于B不再引用D了,盡管A又引用了D,但是因為A已經標記為黑色,GC不會再遍歷A了,所以D會被標記為白色,最后被當做垃圾回收。
    可以看到錯標的結果比漏表嚴重的多,浮動垃圾可以下次GC清理,而把不該回收的對象回收掉,將會造成程序運行錯誤。

    錯標只有在滿足下面兩種情況下才會發生:
    只要打破任一條件,就可以解決錯標的問題,以下為兩種解決方法:原始快照和增量更新,通過寫屏障來記錄。

    原始快照和增量更新
    原始快照打破的是第一個條件:當灰色對象指向白色對象的引用被斷開時,就將這條引用關系記錄下來。當掃描結束后,再以這些灰色對象為根,重新掃描一次。相當于無論引用關系是否刪除,都會按照剛開始掃描時那一瞬間的對象圖快照來掃描。

    增量更新打破的是第二個條件:當黑色指向白色的引用被建立時,就將這個新的引用關系記錄下來,等掃描結束后,再以這些記錄中的黑色對象為根,重新掃描一次。相當于黑色對象一旦建立了指向白色對象的引用,就會變為灰色對象。

    寫屏障
    這個寫屏障指的可不是并發編程里的寫屏障哦!這里的寫屏障指的是屬性賦值的前后加入一些處理,類似于AOP。

    CMS采用的方案就是:寫屏障+增量更新來實現的,打破的是第二個條件:
    當黑色指向白色的引用被建立時,通過寫屏障來記錄引用關系,等掃描結束后,再以引用關系里的黑色對象為根重新掃描一次即可。

    偽代碼大致如下:

    class A{private D d;public void setD(D d) {writeBarrier(d);// 插入一條寫屏障this.d = d;}private void writeBarrier(D d){// 將A -> D的引用關系記錄下來,后續重新掃描} }

    為什么CMS采用“標記-清除”算法而不采用“標記-整理”算法

    因為CMS作為第一款實現用戶線程和收集線程并發執行的收集器!當時的設計理念是減少停頓時間,最好是能并發執行!但是問題來了,如要用戶線程也在執行,那么就不能輕易的改變堆中對象的內存地址!不然會導致用戶線程無法定位引用對象,從而無法正常運行!而標記整理算法和標記復制算法都會移動存活的對象,這就與上面的策略不符!因此CMS采用的是標記清理算法!

    結語

    CMS為了讓GC線程和用戶線程一起工作,回收的算法和過程比以前舊的收集器要復雜很多。究其原因,就是因為GC標記對象的同時,用戶線程還在修改對象的引用關系。因此CMS引入了三色算法,將對象標記為黑、灰、白三種顏色的對象,并通過「寫屏障」技術將用戶線程修改的引用關系記錄下來,以便在「重新標記」階段可以修正對象的引用。
    雖然CMS從來沒有被JDK當做默認的垃圾收集器,存在很多的缺點,但是它開啟了「GC并發收集」的先河,為后面的收集器提供了思路,光憑這一點,就依然值得記錄下來。

    參考:https://blog.csdn.net/qq_32099833/article/details/109558171

    G1收集器

    從JDK(1.3)開始,HotSpot團隊一直努力朝著高效收集、減少停頓(STW: Stop The World)的方向努力,也貢獻了從串行Seria收集器、到并行收集器Parallerl收集器,再到CMS并發收集器,乃至如今的G1在內的一系列優秀的垃圾收集器。

    G1(Garbage First)垃圾收集器是當今垃圾回收技術最前沿的成果之一。早在JDK7就已加入JVM的收集器大家庭中,成為HotSpot重點發展的垃圾回收技術。同優秀的CMS垃圾回收器一樣,G1也是關注最小時延的垃圾回收器,也同樣適合大尺寸堆內存的垃圾收集,官方也推薦使用G1來代替選擇CMS

    G1 (Garbage-First)是?款?向服務器的垃圾收集器,主要針對配備多顆處理器及?容量內存的機器。以極?概率滿?GC停頓時間要求的同時,還具備?吞吐量性能特征。被視為JDK1.7中HotSpot虛擬機的?個重要進化特征。

    G1收集器采用“標記-復制”和“標記-整理”。從整體上看是基于“標記-整理”,從局部看,兩個region之間是“標記-復制”

    它具備以下特點:

    ①分區收集:雖然G1可以不需要其他收集器配合就能獨?管理整個GC堆,但是還是保留了分代的概念。G1最大的特點是引入分區的思路,弱化了分代的概念。

    ②并?與并發:G1能充分利?CPU、多核環境下的硬件優勢,使?多個CPU(CPU或者CPU核?)來縮短Stop-The-World停頓時間。部分其他收集器原本需要停頓Java線程執?的GC動作,G1收集器仍然可以通過并發的?式讓java程序繼續執?。

    ③算法,空間整合:與CMS的“標記–清理”算法不同,G1從整體來看是基于“標記整理”算法實現的收集器;從局部上來看是基于“復制”算法實現的。

    ④可預測的停頓:這是G1相對于CMS的另?個?優勢,降低停頓時間是G1 和 CMS 共同的關注點,但G1 除了追求低停頓外,還能建?可預測的停頓時間模型,能讓使?者明確指定在?個?度為M毫秒的時間?段內,消耗在垃圾收集上的時間不得超過N毫秒。

    G1的堆內存算法

    G1之前的JVM內存模型

    • 新生代:伊甸園區(eden space) +2個幸存區
    • 老年代
    • 持久代(perm space): JDK1.8之前
    • 元空間(metaspace): JDK1.8之后取代持久代

    G1收集器的內存模型

    1)G1堆內存結構:
    堆內存會被切分成為很多個固定大小區域(Region),每個是連續范圍的虛擬內存。
    堆內存中一個區域(Region)的大小可以通過-XX:G1HeapRegionSize參數指定,大小區間最小1M、最大32M,總之是2的冪次方。

    默認把堆內存按照2048份均分。

    2)G1堆內存分配
    每個Region被標記了E、S、O和H,這些區域在邏輯上被映射為Eden,Survivor和老年代。
    存活的對象從一個區域轉移(即復制或移動)到另一個區域。區域被設計為并行收集垃圾,可能會暫停所有應用線程。
    如上圖所示,區域可以分配到Eden,survivor和老年代。此外,還有第四種類型,被稱為巨型區域(Humongous Region),Humongous區域是為了那些存儲超過50%標準region大小的對象而設計的,它用來專門存放巨型對象。如果一個H區裝不下一個巨型對象,那么G1會尋找連續的H分區來存儲。為了能找到連續的H區,有時候不得不啟動Full GC。
    G1收集器采用“標記-復制”和“標記-整理”。從整體上看是基于“標記-整理”,從局部看,兩個region之間是“標記-復制”。

    G1回收流程

    在執行垃圾收集時,G1以類似于CMS收集器的方式運行。

    G1收集器的階段分以下幾個步驟:

    1)G1執行的第一階段:初始標記(Initial Marking )
    這個階段是STW(Stop the World )的,所有應用線程會被暫停,標記出從GC Root開始直接可達的對象。

    2)G1執行的第二階段:并發標記
    從GC Roots開始對堆中對象進行可達性分析,找出存活對象,耗時較長。當并發標記完成后,開始最終標記(Final Marking )階段

    3)最終標記(標記那些在并發標記階段發生變化的對象,將被回收),這階段需要停頓線程,但是可以并行執行。

    4)篩選回收
    暫停用戶線程,篩選階段首先對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間來制定回收計劃。

    G1收集器在后臺維護了?個優先列表,每次根據允許的收集時間,優先選擇回收價值最?的Region(這也就是它的名字Garbage-First的由來)。這種使?Region劃分內存空間以及有優先級的區域回收?式,保證了GF收集器在有限時間內可以盡可能?的收集效率(把內存化整為零)。

    對各個Region的回收價值和成本進行排序,根據用戶所期望的停頓時間來制定回收計劃,可以自由選擇任意多個Region構成回收集,然后把決定回收的那一部分Region的存活對象復制到空的Region中,再清理掉個舊的Region的全部空間。這里的操作涉及存活對象的移動,是必須暫停用戶線程,由多條收集器線程并行完成的

    G1收集器只有并發標記不會stop the world,而CMS并發標記和并發清除不會stop the world

    最后,G1中還提供了兩種垃圾回收模式,Young GC和Mixed GC,兩種都是Stop The World(STW)的。

    G1的GC模式

    1.YoungGC年輕代收集
    在分配一般對象(非巨型對象)時,當所有eden region使用達到最大閥值并且無法申請足夠內存時,會觸發一次YoungGC。每次younggc會回收所有Eden以及Survivor區,并且將存活對象復制到Old區以及另一部分的Survivor區。YoungGC的回收過程如下:

    • 根掃描,跟CMS類似,Stop the world,掃描GC Roots對象。
    • 處理Dirty card,更新RSet.
    • 掃描RSet,掃描RSet中所有old區對掃描到的young區或者survivor去的引用。
    • 拷貝掃描出的存活的對象到survivor2/old區
    • 處理引用隊列,軟引用,弱引用,虛引用

    2.mixed gc
    當越來越多的對象晉升到老年代old region時,為了避免堆內存被耗盡,虛擬機會觸發一個混合的垃圾收集器,即mixed gc,該算法并不是一個old gc,除了回收整個young region,還會回收一部分的old region,這里需要注意:是一部分老年代,而不是全部老年代,可以選擇哪些old region進行收集,從而可以對垃圾回收的耗時時間進行控制。
    G1沒有fullGC概念,需要fullGC時,調用serialOldGC進行全堆掃描(包括eden、survivor、o、perm)。

    參考:https://mikechen.cc/7126.html

    總結

    以上是生活随笔為你收集整理的垃圾回收器CMS和G1的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。