java jni原理_JVM源码分析之由JNI操作引起的迷惑性GC

简书 占小狼

转载请注明原创出处,谢谢!

由于JNI操作中的GC locker,会导致一次正常的YGC变得扑朔迷离,还不清楚GC locker的可以看看传送门 - JVM源码分析之GC locker深度分析

下面使用3个线程来讲解在eden区申请内存不够时的各种情况

1、线程A和B往eden区塞对象

2、线程C负责执行JNI方法

假设之前一次GC都没有发生过,线程A在eden区分配出现空间不足,这时会触发一次Allocation Failed(VM_GenCollectForAllocation)的GC,但这次GC最终能不能执行取决于GC locker的状态。

如果这个时候,线程C正在临界区(critical region)里面,那由线程A分配失败引起的YGC的就会被丢弃,设置_need_gc标识,标记线程C在离开临界区的时候需要触发一次GC动作。

6b215dbd206139f7902b9fd62f2c672a.png

由于放弃了本次的YGC,如果不是TLAB,会尝试在老年代进行分配,并返回,所以一次JNI操作可以会引起正常的对象直接分配在老年代的空间,这时GC_locker::is_active_and_needs_gc()已经成立了,会设置一个gc locker,即_gc_locked赋值为true。

到这里,整个YGC就结束了,当然本次YGC什么都没有回收,GC日志也什么都不会打印,只是设置了一些关键标识,_gc_locked和_need_gc之类的,供后续逻辑使用。

这时线程B也来申请内存,(线程C还在临界区),自然eden区的空间是不足的,当然不能茹莽的直接去触发一次YGC,JVM也没这么蠢,首先会进行如下判断,看看是不是可以直接在老年代进行分配:

11aa8e098eae49b581fcfa28fe2c988c.png

这里有一个判断逻辑should_try_older_generation_allocation,实现如下:

2fbd8c8f8ff55fea9c14323466ecdf04.png

这里有一个条件是我们目前需要关心的,GC_locker::is_active_and_needs_gc(),从上面的分析来看,这个条件肯定是成立的,所以结果是可以在老年代进行内存分配。

内存分配attempt_allocation(size, is_tlab, first_only)的实现:

e03d67fb804c1d8563bb8acb0ea5659e.png

很显然,目前参数first_only为false,在新生代的allocate动作不可能申请成功,接着会尝试在老年代申请。

如果万一,老年代也没有足够的内存,一般情况下不会发生,不过极端情况我们也是需要考虑的,真的发生的话,还会尝试进行扩容再分配。

如果已经不能扩容了,则执行下面的逻辑:

45852b0b6c8310d7fc328d47696e863b.png

很显然,线程B当前并没有在临界区,则执行GC_locker::stall_until_clear(),接着看这个方法的实现:

61d71e8ecf99d7e8f6d2b00e873668a0.png

这里用到了之前设置的_need_gc参数,线程B就在JNICritical_lock锁上等待。

如果添加了-XX+PrintJNIGCStalls参数,那么由于JNI引起的一些奇奇怪怪GC问题就好查多了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值