简书 占小狼,转载请注明原创出处,谢谢!
有时在分析GC日志的时候,会看到在一次正常的"Allocation Failure" GC之后,紧跟一次"GCLocker Initiated GC",而且此时年轻代的使用量非常低,大概的GC日志可以看看下面这个。
可以看到,在发生"GCLocker Initiated GC"时,eden区只使用了1%,毫无疑问,这是一次没有意义的YGC,那为什么会这样呢?
这得从JNI的GCLocker的相关实现说起,之前文章中已经提过,当使用JNI访问JVM中的字符串时,会使用一对Get/ReleaseStringCritical函数,内部由GCLocker的lock/unlock critical实现,其中unlock critical的实现逻辑如下:
假设一个场景:
1、线程T1执行JNI方法,进入critical之后会执行unlock方法,如果它是最后一个离开critical的线程,则会触发一次GC operation(OP1)进行一次GC locker-initiated young gc.
2、线程T2负责在年轻代申请内存,在申请失败时,会触发一次GC operation(OP2)进行一次 allocation failure young gc.
因为线程是并发执行的,所以存在以下两种情况:
1、如果线程T1还没执行到图中的A位置,那么这个时候OP2触发的YGC会被丢弃,这种情况是没有问题的.
2、如果线程T1执行在A到B的位置,这时_jni_lock_count
已经减为0,则GC_Locker::is_active()
为false,OP2触发的YGC会被正常的执行,并且会阻塞住OP1触发的YGC,等之前的YGC结束之后,OP1的YGC继续执行,在这种情况,显然是多余的一次。
其实这是openJDK的一个bug,只是一直没被修复,bug地址