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比較快呢?
- Young Generation一般比較小
- Minor GC的時(shí)候不需要掃描Old Generation
- 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)行處理。
- Initial Mark (需要stop the world)
這個(gè)是標(biāo)記Old Generation中被Young Generation引用的對象。這部分時(shí)間比較短。 - Concurrent Marking
遞歸的遍歷Old Generation中被已經(jīng)標(biāo)記為live的對象引用的對象和root引用的對象。這一步是和業(yè)務(wù)線程并行的。 - Remark (需要stop the world)
尋找第二步中由于并行運(yùn)行而漏掉的live的對象。 - Concurrent Sweep
清理沒有被標(biāo)記的對象。 - 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

- 每個(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)
- 因?yàn)閮?nèi)存塊不是連續(xù)的,而是由一些小內(nèi)存塊組成的,所以可以比較容易的調(diào)整eden和survivor區(qū)的大小
- 使用remember set來減少gc時(shí)掃描的region的數(shù)目
- 使用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í)間。

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í)間。