You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么JNI的FindClass方法会出现不符合预期的奇怪副作用?

问题根本原因

该异常现象的核心根因是违规持有JNI局部引用跨native方法调用,触发了JNI规范明确的未定义行为,具体逻辑如下:

  • JNI规范明确规定:FindClass接口默认返回局部引用,局部引用的生命周期仅绑定在当前触发的native方法调用上下文内,一旦当前native方法执行完成返回Java层,所有未手动转为全局引用的局部引用会被自动释放,对应的引用指针会变为悬空指针,后续访问属于未定义行为。
  • 代码中test0方法内将FindClass返回的Integer类局部引用直接赋值给全局变量Integer_class_0,本身就是违反JNI规范的错误写法:test0执行返回后Integer_class_0已经是失效的悬空引用,后续在test1中访问该引用的结果完全取决于JVM的内部内存复用逻辑,没有确定性。
  • 该问题在JDK 16上可稳定复现的原因是JDK 9+对JNI局部引用表的回收复用逻辑做了优化,失效引用槽位的复用概率大幅提升,更容易暴露这类违规使用的问题。
  • 注释(*)行时,test1执行过程中JVM将Integer_class_0原来指向的局部引用槽位复用给了其他对象,此时拿着悬空引用调用equals方法,实际是拿一个非Integer.class的对象和有效Integer.class比对,自然返回false。
  • 取消(*)行注释时,test1开头调用的FindClass("java/lang/Integer")刚好被JVM分配到了Integer_class_0原来指向的内存槽位,此时失效指针刚好又指向了有效Integer.class对象,所以equals返回true,这个结果只是巧合,并不代表代码逻辑正确。
  • 输出中(b)行一直返回true的原因是arg_type和Integer_class_1都是在test1同一个native调用上下文中获取的有效局部引用,指向同一个Integer.class对象,比对结果符合预期。
修复方案

只要将test0中获取的Integer类引用转为全局引用即可解决问题:

// 原错误写法
// Integer_class_0 = (*env)->FindClass(env, "java/lang/Integer");
// 修正为
Integer_class_0 = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/lang/Integer"));

注意程序退出前需要调用DeleteGlobalRef(env, Integer_class_0)释放全局引用,避免内存泄漏。

内容的提问来源于stack exchange,提问作者Luke Hutchison

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 07:06:02