1、JVM垃圾收集器發(fā)展過程
1、第一階段:Serial(串行)收集器
特點:單線程收集器,垃圾回收時,必須暫停其他所有工作線程。
2、第二階段:Parallel(并行)收集器
特點:充分利用多核特性,使用多線程完成清理工作。
3、第三階段:CMS(并發(fā))收集器
特點:垃圾收集線程和用戶線程,可以同時執(zhí)行。
缺點:CPU資源敏感;無法處理浮動垃圾;大量內(nèi)存碎片
初始標記:【需要停頓】。只標記GC ROOTS能直接關(guān)聯(lián)到的對象,速度很快
并發(fā)標記:與用戶線程同時執(zhí)行。標記GC ROOTS所有能關(guān)聯(lián)到的對象。
重新標記:【需要停頓】。修正并發(fā)標記期間,因用戶程序執(zhí)行導致標記變動的記錄。
并發(fā)清除:與用戶線程同時執(zhí)行,清理標記的垃圾對象。會產(chǎn)生浮動垃圾
4、第四階段:G1(Garbage-First)(并發(fā))收集器
特點:引入分區(qū)(分治、部分收集),弱化分代。關(guān)注最小時延,適合大尺寸堆內(nèi)存。應(yīng)用在多處理器和大容量內(nèi)存環(huán)境中,盡量縮短處理超大堆(大于4GB)時,產(chǎn)生的停頓。帶有整理功能,相對CMS內(nèi)存碎片的產(chǎn)生率大大降低;STW可控,停頓時間上加了預測機制
2、G1收集器對比CMS
1、算法:
基于標記-整理算法,碎片產(chǎn)生率降低。CMS有可能出現(xiàn)分配大對象時,無法得到連續(xù)空間而導致提前觸發(fā)一次full gc。
2、停頓時間可控:通過設(shè)置預期停頓時間,控制gc時間,避免應(yīng)用雪崩。
3、收集區(qū)域:G1雖然保留了新老代的概念,但是收集器是以整個區(qū)域為單位收集的。G1可以在Young GC中使用,CMS只能在Old區(qū)(常搭配ParNew收集年輕代)。
4、Remark階段高效:RSet解決跨代引用(如Old區(qū)域引用Young區(qū)域?qū)ο?、SATB算法記錄漏標對象。
1.G1之前JVM內(nèi)存模型
image.png
2.G1收集器的內(nèi)存模型
Humongous:存放大對象(超過Region容量的一半)。
image.png
4、G1重點參數(shù)
-XX:+UseG1GC:啟用G1收集器
-XX: G1HeapRegionSize:設(shè)置Region分區(qū)大小。1M~32M,size越大垃圾存活時間長,gc間隔長,但gc的持續(xù)時間邊長
-XX: MaxGCPauseMillis:最大GC停頓時間,軟目標。
3、G1收集器底層原理
1)G1運作過程
1.初始標記:標記GC Roots能直接關(guān)聯(lián)到的對象。修改TAMS指針(Top at Mark Start),【G1為每個Region分配了兩個指針,用于記錄回收過程中新對象的分配】,短暫停頓用戶線程,借用Minor GC時完成。
2.并發(fā)標記:從GC Roots開始遞歸掃描整個堆,對堆中對象進行可達性分析。【與用戶線程并發(fā)執(zhí)行,并發(fā)過程漏標對象使用SATB(snapshot-at-the-beginning)算法記錄】
3.最終標記:短暫停頓用戶線程,處理并發(fā)階段遺留的漏標對象。
4.篩選回收:對各個Region的回收價值和成本進行排序,根據(jù)用戶期望的停頓時間,制定回收計劃,回收一部分Region。【兩種回收模式,Young GC、Mixed GC】
image.png
2)Young GC回收過程
分配一般對象(非巨型對象)時,所有E區(qū)使用達最大閾值且無法申請到足夠內(nèi)存時,進行一次Young GC。回收所有S區(qū)和E區(qū),將存活對象復制到O和另外S區(qū)。
1.根掃描(初始標記)。STW,掃描GC Roots對象
2.處理Dirty Card,更新Rset。
3.掃描Rset。掃描Rset中所有Old區(qū)對Young區(qū)、S區(qū)的引用。
4.對象拷貝。拷貝存活對象到S區(qū)和Old區(qū)。
3)Mixed GC回收過程
當越來越多對象晉升到O區(qū)時,為避免內(nèi)存耗盡,虛擬機會觸發(fā)mixed gc。回收整個E和S區(qū),根據(jù)用戶期望停頓時間,回收一部分O區(qū)。
4、G1底層支持
1)三色標記與漏標問題
三色標記
黑色:跟對象;或者該對象和他的子對象都被掃描過。
灰色:本身被掃描,但還未掃描該對象的子對象。
白色:未被掃描的對象;掃描完所有對象后,白色為不可達對象(垃圾對象)
漏標問題
image.png
CMS中的解決方案:Incremental Update算法:當一個白色的對象被一個黑色對象引用,【將黑色對象重新標記為灰色】,讓垃圾回收器重新掃描。
G1中的解決方案:SATB(snapshot-at-the-beginning):當B->C的引用鏈消失時,將C推到GC的堆棧上,保證C還能被GC掃描到。
2)Rset解決跨代引用
現(xiàn)象:如果老年代引用了新生代的對象,那么回收新生代時,要跟蹤老年代到新生代的所有引用。
image.png
Remembered Set:Hash表,Key是Region的地址,Value是對象的卡頁集合。【其他Region中的對象引用本Region中對象的關(guān)系,表示誰引用了我的對象】
CardTable:如果一個O區(qū)CardTable中有對象指向Y區(qū),就將它設(shè)置為Dirty,下次掃描時,只需要掃描CardTable上是Dirty的內(nèi)存區(qū)域即可。
3)TAMS(Top at Mark Start)指針
G1為每個Region區(qū)域設(shè)計了兩個TASM指針。要達到與用戶線程并發(fā)運行,必須要解決回收過程中新對象的分配。從Region區(qū)域劃分出一部分空間,用于記錄并發(fā)回收過程中的新對象產(chǎn)生,不納入垃圾回收范圍。
4)安全點與安全區(qū)域
安全點:用戶線程暫停,GC線程開始工作,要確保用戶暫停的這行字節(jié)碼指令,【不會導致引用關(guān)系的變化】,如【方法調(diào)用、循環(huán)跳轉(zhuǎn)、異常跳轉(zhuǎn)等】
主動式中斷:設(shè)置一個標志,各用戶線程主動輪訓這個標志,True則在最近的安全點上主動中斷掛起。
安全區(qū)域:如果業(yè)務(wù)線程處于Sleep或者Blocked狀態(tài),程序則沒辦法進入安全點。作為安全點的擴展【確保在某段代碼中,引用關(guān)系不會發(fā)生變化】