簡書 占小狼
轉(zhuǎn)載請注明原創(chuàng)出處帘饶,謝謝亿卤!
周末抽空把YGC的源碼實現(xiàn)重新看了一遍叨襟,發(fā)現(xiàn)細節(jié)遠比知道的多...
首先要知道络它,什么情況會導致YGC的發(fā)生禾进?最常見的情況是在年輕代分配內(nèi)存時锈嫩,出現(xiàn)空間不足待逞,這里的內(nèi)存分配甥角,有可能是TLAB,也有可能是一個對象(該對象在TLAB中放不下识樱,但虛擬機不想重新申請TLAB嗤无,就在Eden區(qū)分配)
1、如果觸發(fā)的YGC順利執(zhí)行完怜庸,期間沒有發(fā)生任何問題当犯,垃圾回收完成后,正常的分配內(nèi)存割疾。
2嚎卫、如果YGC剛要開始執(zhí)行,卻不幸的發(fā)生了JNI的GC locker宏榕,本次的YGC會被放棄拓诸,如果是給對象分配內(nèi)存,會在老年代中直接分配內(nèi)存麻昼,如果是TLAB的話奠支,就要等JNI結(jié)束了。
3抚芦、如果沒有JNI的干擾倍谜,在YGC過程中迈螟,對象年紀達到閾值,正常晉升尔崔,或to空間不足答毫,對象提前晉升,但老年代又沒這么多空間容納晉升上來的對象季春,這時會發(fā)生“promotion failed”洗搂,而且eden和from區(qū)的空間沒辦法清空, 把from區(qū)和to區(qū)進行swap鹤盒,所以當前eden和from的使用率都是接近100%的蚕脏,如果當前是給對象(非TLAB)申請內(nèi)存,會繼續(xù)觸發(fā)一次老年代的回收動作侦锯,下面是一個例子:
/**
* -Xmx20m -Xms20m -Xmn14m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
*-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=75
*-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC
*/
public class JVM {
private static final int _1MB = 1024 * 1024;
private static final int _1K = 1024;
public static void main(String[] args) throws Exception {
byte[][] arr = new byte[10000][];
for (int i = 0; i< 1200; i++) {
arr[i] = new byte[10* _1K];
}
System.in.read();
}
}
把年輕代設置的大點驼鞭,制造“promotion failed”,下面是gc日志:
1尺碰、“promotion failed” 如期到來挣棕。
2、對老年代進行了一次回收亲桥,這次回收有兩種方式洛心,一種是compact,另一種是mark-sweep题篷,顯然第一種會進行內(nèi)存的壓縮操作词身,第二種只進行標記清除。到底會使用哪一種番枚,會進行如下判斷:
其中UseCMSCompactAtFullCollection
默認是開啟的法严,而且CMSFullGCsBeforeCompaction
默認是0,所以如果沒有重新設置該參數(shù)的話葫笼,按理說深啤,每次都是會進行compact操作,如果CMSFullGCsBeforeCompaction
被設置成一個大于0的值路星,還有其它條件可以導致compact溯街,一個是如System.gc
,另一個是發(fā)生了promotion failed
洋丐,還有其它等等呈昔。
本例子中雖然使用了-XX:+UseConcMarkSweepGC
,但是不會使用并發(fā)的CMS算法友绝,如果當前CMS的background collect已經(jīng)開始執(zhí)行堤尾,當前GC線程會搶過執(zhí)行權(quán),并記錄“concurrent mode failed”九榔。
如果需要compact,采用單線程的Serial GC進行回收,該算法實現(xiàn)在genMarkSweep.cpp
哲泊。
如果不需要compact剩蟀,則執(zhí)行一個標記-清除的過程,實現(xiàn)在CMSCollector::collect_in_foreground
中切威。
3育特、同樣對永久帶也來了一次回收
從這個打印出來的日志可以發(fā)現(xiàn),本次的YGC是分配對象時觸發(fā)的先朦,而不是TLAB(因為在JVM運行過程中缰冤,會動態(tài)調(diào)整TLAB的大小和最大浪費空間),雖然日志中沒有FULL GC
的字樣喳魏,其實執(zhí)行的就是一次full gc過程棉浸。
本來我一直納悶為啥TLAB的分配,會導致老年代的回收刺彩,因為如果是TLAB的話迷郑,老年代的should_allocate方法實現(xiàn)如下:
virtual bool should_allocate(size_t word_size, bool is_tlab) {
bool result = false;
size_t overflow_limit = (size_t)1 << (BitsPerSize_t - LogHeapWordSize);
if (!is_tlab || supports_tlab_allocation()) {
result = (word_size > 0) && (word_size < overflow_limit);
}
return result;
}
其中is_tlab
為true,supports_tlab_allocation()
為false创倔,所以不會繼續(xù)對老年代進行回收嗡害。
漸行漸遠,細節(jié)遠不止這些畦攘,不要在細節(jié)中迷失了自己...