簡(jiǎn)書(shū) 占小狼,轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處句喷,謝謝亲怠!
有時(shí)在分析GC日志的時(shí)候,會(huì)看到在一次正常的"Allocation Failure" GC之后镊叁,緊跟一次"GCLocker Initiated GC"尘颓,而且此時(shí)年輕代的使用量非常低,大概的GC日志可以看看下面這個(gè)晦譬。
可以看到疤苹,在發(fā)生"GCLocker Initiated GC"時(shí),eden區(qū)只使用了1%蛔添,毫無(wú)疑問(wèn)痰催,這是一次沒(méi)有意義的YGC兜辞,那為什么會(huì)這樣呢?
這得從JNI的GCLocker的相關(guān)實(shí)現(xiàn)說(shuō)起夸溶,之前文章中已經(jīng)提過(guò)逸吵,當(dāng)使用JNI訪問(wèn)JVM中的字符串時(shí),會(huì)使用一對(duì)Get/ReleaseStringCritical函數(shù)缝裁,內(nèi)部由GCLocker的lock/unlock critical實(shí)現(xiàn)扫皱,其中unlock critical的實(shí)現(xiàn)邏輯如下:
假設(shè)一個(gè)場(chǎng)景:
1、線程T1執(zhí)行JNI方法捷绑,進(jìn)入critical之后會(huì)執(zhí)行unlock方法韩脑,如果它是最后一個(gè)離開(kāi)critical的線程,則會(huì)觸發(fā)一次GC operation(OP1)進(jìn)行一次GC locker-initiated young gc.
2粹污、線程T2負(fù)責(zé)在年輕代申請(qǐng)內(nèi)存段多,在申請(qǐng)失敗時(shí),會(huì)觸發(fā)一次GC operation(OP2)進(jìn)行一次 allocation failure young gc.
因?yàn)榫€程是并發(fā)執(zhí)行的壮吩,所以存在以下兩種情況:
1进苍、如果線程T1還沒(méi)執(zhí)行到圖中的A位置,那么這個(gè)時(shí)候OP2觸發(fā)的YGC會(huì)被丟棄鸭叙,這種情況是沒(méi)有問(wèn)題的.
2觉啊、如果線程T1執(zhí)行在A到B的位置,這時(shí)_jni_lock_count
已經(jīng)減為0沈贝,則GC_Locker::is_active()
為false杠人,OP2觸發(fā)的YGC會(huì)被正常的執(zhí)行,并且會(huì)阻塞住OP1觸發(fā)的YGC宋下,等之前的YGC結(jié)束之后嗡善,OP1的YGC繼續(xù)執(zhí)行,在這種情況学歧,顯然是多余的一次滤奈。
其實(shí)這是openJDK的一個(gè)bug,只是一直沒(méi)被修復(fù)撩满,bug地址