Java GC理解

GC分類


  • Minor GC - 清理Young Generation
  • Major GC - 清理Old Generation
  • Full GC - 全部清理
    其實(shí)這3個(gè)很難分開,有的Minor GC會觸發(fā)Major GC,那算不算一次FGC呢?

jstat中FGC和FGCT是什么含義

jstat中的FGC的次數(shù)是stop the word的次數(shù),比如cms的一次gc中有兩次stop the word,那么一次gc過程jstat顯示的FGC次數(shù)就會加2,F(xiàn)GCT是指的stop the word花費(fèi)的時(shí)間。其實(shí)cms是一次Major GC的過程,但是jstat卻顯示了兩次FGC,是不是坑呢?
參考Minor GC、Major GC和Full GC之間的區(qū)別

Minor GC


因?yàn)閙inor gc對程序的影響一般比較小,所以大家一般都比較少的關(guān)注minor gc,但是Minor GC還是會stop the world的。下面簡單的探討一下Minor GC。

大家比較少關(guān)注minor gc的一個(gè)原因是minor gc執(zhí)行比較快。

為什么MinorGC比較快呢?

  1. Young Generation一般比較小
  2. Minor GC的時(shí)候不需要掃描Old Generation
  3. Young Generation中大部分都是沒用的,GC中并不需要拷貝到Survivor或者Old Generation。

Minor GC的時(shí)候?yàn)槭裁床恍枰獟呙鐿ld Generation

Old Generation中有一個(gè)數(shù)據(jù)結(jié)構(gòu)Card Table,這個(gè)數(shù)據(jù)結(jié)構(gòu)記錄了Old Generation中引用Young Generation的情況,所以Minor GC的時(shí)候只需要檢查這里即可,不需要掃描整個(gè)Old Generation。

CMS GC


下面描述的算法只是對 Old Generation進(jìn)行處理。

  1. Initial Mark (需要stop the world)
    這個(gè)是標(biāo)記Old Generation中被Young Generation引用的對象。這部分時(shí)間比較短。
  2. Concurrent Marking
    遞歸的遍歷Old Generation中被已經(jīng)標(biāo)記為live的對象引用的對象和root引用的對象。這一步是和業(yè)務(wù)線程并行的。
  3. Remark (需要stop the world)
    尋找第二步中由于并行運(yùn)行而漏掉的live的對象。
  4. Concurrent Sweep
    清理沒有被標(biāo)記的對象。
  5. Resetting
    一次GC完成,重置數(shù)據(jù)結(jié)構(gòu)
516357.260: [GC [1 CMS-initial-mark: 4406638K(6291456K)] 4421249K(10136256K), 0.0226190 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 
516357.283: [CMS-concurrent-mark-start]
516357.407: [CMS-concurrent-mark: 0.124/0.124 secs] [Times: user=1.72 sys=0.31, real=0.12 secs] 
516357.407: [CMS-concurrent-preclean-start]
516357.455: [CMS-concurrent-preclean: 0.047/0.048 secs] [Times: user=0.27 sys=0.08, real=0.05 secs] 
516357.456: [CMS-concurrent-abortable-preclean-start]
516360.647: [CMS-concurrent-abortable-preclean: 3.174/3.191 secs] [Times: user=14.11 sys=3.22, real=3.19 secs] 
516360.653: [GC[YG occupancy: 1770775 K (3844800 K)]516360.653: [Rescan (parallel) , 0.2765090 secs]516360.930: [weak refs processing, 0.2960360 secs] [1 CMS-remark: 4406638K(6291456K)] 6177413K(10136256K), 0.5742240 secs] [Times: user=13.81 sys=0.00, real=0.58 secs] 
516361.228: [CMS-concurrent-sweep-start]
516365.291: [CMS-concurrent-sweep: 4.036/4.064 secs] [Times: user=21.48 sys=5.05, real=4.06 secs] 
516365.292: [CMS-concurrent-reset-start]
516365.317: [CMS-concurrent-reset: 0.025/0.025 secs] [Times: user=0.11 sys=0.03, real=0.03 secs]

從這個(gè)過程中能看出,CMS并沒有compaction,就是把live的對象集中到一起,那么就有可能造成內(nèi)存碎片,所以在使用cms回收器的時(shí)候,一般會帶上useCMSCompactAtFullCollection,就是在FGC的時(shí)候進(jìn)行compaction。

ParNewGC


我們使用cms gc算法的時(shí)候,新生代的gc算法就是并行g(shù)c算法。

  • MaxTenuringThreshold=6
    MaxTenuringThreshold這個(gè)參數(shù)用于控制對象能經(jīng)歷多少次Minor GC才晉升到老生代,但是并不是對象必須經(jīng)歷MaxTenuringThreshold才會晉升到老生代,jvm會動態(tài)計(jì)算可以參考這個(gè)
  • SurvivorRatio
    survivor區(qū)占新生代的比例
[Times: user=0.21 sys=0.00, real=0.02 secs] 
608904.697: [GC 608904.699: [ParNew
Desired survivor size 178946048 bytes, new threshold 6 (max 6)
- age   1:    2477432 bytes,    2477432 total
- age   2:     155536 bytes,    2632968 total
- age   3:       4688 bytes,    2637656 total
- age   4:       5904 bytes,    2643560 total
- age   5:       5312 bytes,    2648872 total
- age   6:      10656 bytes,    2659528 total
: 3498496K->3772K(3844800K), 0.0151990 secs] 6053989K->2559272K(10136256K), 0.0172310 secs]

G1 (Garbage First) GC


G1 Heap
  • 每個(gè)region大小相同
  • 每個(gè)region分別屬于 eden survior和 old之一

Region的大小

一個(gè)Region的大小可以通過參數(shù)-XX:G1HeapRegionSize設(shè)定,取值范圍從1M到32M,且是2的指數(shù)。如果不設(shè)定,那么G1會根據(jù)Heap大小自動決定。如果是jvm自動計(jì)算,會以分為2048左右個(gè)Region為目標(biāo),計(jì)算Region的大小。

GC過程

G1提供了兩種GC模式,Young GC和Mixed GC,兩種都是會Stop The World的。

  • Young GC:選定所有年輕代里的Region。通過控制年輕代的region個(gè)數(shù),即年輕代內(nèi)存大小,來控制young GC的時(shí)間開銷。
  • Mixed GC:選定所有年輕代里的Region,外加根據(jù)global concurrent marking統(tǒng)計(jì)得出收集收益高的若干老年代Region。在用戶指定的開銷目標(biāo)范圍內(nèi)盡可能選擇收益高的老年代Region。

由上面的描述可知,Mixed GC不是full GC,它只能回收部分老年代的Region,如果mixed GC實(shí)在無法跟上程序分配內(nèi)存的速度,導(dǎo)致老年代填滿無法繼續(xù)進(jìn)行Mixed GC,就會使用serial old GC(full GC)來收集整個(gè)GC heap。所以我們可以知道,G1是不提供full GC的。

GC特點(diǎn)

  1. 因?yàn)閮?nèi)存塊不是連續(xù)的,而是由一些小內(nèi)存塊組成的,所以可以比較容易的調(diào)整eden和survivor區(qū)的大小
  2. 使用remember set來減少gc時(shí)掃描的region的數(shù)目
  3. 使用SATB算法,保證gc的時(shí)候的正確性

RSet(Remember Set)

RSet存儲的是有引用我這個(gè)region的card table的index(card是一個(gè)更小的單元,一般是512B,所以一個(gè)region一般有多個(gè)card),有了這個(gè)數(shù)據(jù)結(jié)構(gòu),如下圖(出處)所示,那么如果要回收Region2的對象的話,只需要查看Region2的RSet中的card是否還有指向這個(gè)region的對象即可,不需要掃描全部的region,這樣能大大的節(jié)省時(shí)間。

RSet

concurrent的GC算法

先介紹一下背景知識,根據(jù)三色標(biāo)記算法,我們知道在gc過程中對象存在三種狀態(tài):

白:對象沒有被標(biāo)記到,標(biāo)記階段結(jié)束后,會被當(dāng)做垃圾回收掉。
灰:對象被標(biāo)記了,但是它的field還沒有被標(biāo)記或標(biāo)記完。
黑:對象被標(biāo)記了,且它的所有field也被標(biāo)記完了。

試想一下如果一個(gè)有引用的對象在標(biāo)記算法的時(shí)候,沒有被標(biāo)記上,那么就會在gc算法運(yùn)行結(jié)束的時(shí)候被清除掉,這對一個(gè)gc算法來說是個(gè)災(zāi)難。這種情況發(fā)生有兩個(gè)充分必要條件:

  • Mutator賦予一個(gè)黑對象該白對象的引用。
  • Mutator刪除了所有從灰對象到該白對象的直接或者間接引用。

incremental update

破壞了第一個(gè)條件,如果發(fā)生第一個(gè)情況,那么這個(gè)黑對象要么被改成灰色,要么把白色改成灰色。因?yàn)檫@個(gè)建立的調(diào)用關(guān)系圖在gc的過程中是會增量更新的,所以叫incremental update。

snapshot-at-the-begining

SATB算法破壞了第二個(gè)條件,就是在gc過程中,如果一個(gè)待回收region中的白對象的引用被替換或者刪除了,那么就會保留這個(gè)引用,這樣在標(biāo)記完成后,這個(gè)白對象肯定不會被清楚。當(dāng)然這樣做也有一定的副作用,有可能這個(gè)保留的白對象是個(gè)真正的Garbage,那么就被放過了。為啥叫這個(gè)名字呢?因?yàn)樗⒌恼{(diào)用關(guān)系圖是gc開始的時(shí)候的snapshot,后來沒有update。

G1 為什么比CMS能更好的預(yù)測GC的停頓時(shí)間呢?

CMS要進(jìn)行一次GC,那么就需要掃描Old Generation內(nèi)存中所有的object,但是G1就不需要,他可以選擇一個(gè)Region進(jìn)行GC,所以能較好的控制停頓時(shí)間。

參考文獻(xiàn)


  1. 美團(tuán)關(guān)于G1的分享
  2. Getting Started with the G1 Garbage Collector
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容