JNI全局引用泄漏导致对象无法GC回收,引发逻辑卡死;需通过GC日志、jmap对比、NMT内存分析及JNI日志定位,修复须确保NewGlobalRef/DeleteGlobalRef配对且覆盖异常路径。
这个问题实际指向的是 JNI(Java Native Interface)场景下,本地方法栈中通过
创建的全局引用未调用
释放,导致 Java 对象无法被 GC 回收,进而引发逻辑卡死、资源耗尽或分析链路“永久锁死”——比如监控线程反复尝试获取已被强引用滞留的对象、回调注册失效、或依赖对象生命周期的流程停滞。
确认是否为全局引用泄漏引发的阻塞
全局引用泄漏本身不会直接造成“锁死”,但会间接破坏预期的生命周期控制。需先排除是否真由它引起:
观察 GC 日志:若频繁 Full GC 且老年代持续增长、回收效果差,同时
队列积压,可能是大量对象因全局引用无法回收
用
对比多次 dump,看特定业务类实例数是否只增不减
检查 JNI 代码中所有
调用点,确认每处都有对应路径执行
(尤其注意异常分支、提前 return、多线程竞争场景)
定位 JNI 全局引用持有位置
仅靠 Java 层工具无法直接看到 C/C++ 中的全局引用,需结合 JVM 参数与原生调试手段:
启动时添加
和
,辅助判断对象是否本该被回收却一直存活
使用
查看 NMT(Native Memory Tracking)统计,若 “Internal” 或 “Other” 类别内存持续上涨,提示原生层资源管理异常
在关键 JNI 函数入口/出口插入日志(如打印
后的指针值),配合
或
断点验证引用创建与销毁是否配对
修复与预防策略
根本解决依赖编码规范和运行时防护:
避免在循环或高频调用路径中创建全局引用;优先用局部引用(
)或弱全局引用(
)替代
所有
必须包裹在
块中,确保
在 finally 执行
对跨线程传递的全局引用,加原子计数或引用池管理,防止重复删除或漏删
上线前启用
并定期采集
作基线比对
这类问题隐蔽性强,不表现为典型线程阻塞或死锁,但会导致系统“看似运行正常,实则缓慢腐化”。重点不在“锁”,而在“引用滞留阻断了对象生命周期流转”。
NewGlobalRefDeleteGlobalRefjava.lang.ref.Finalizerjmap -histo:live NewGlobalRefDeleteGlobalRef-XX:+TraceClassLoading-XX:+PrintGCDetailsjcmd VM.native_memory summary env->NewGlobalRef(obj)gdblldbLocalRefNewWeakGlobalRefNewGlobalReftry-finallyDeleteGlobalRef-XX:NativeMemoryTracking=detailjcmd VM.native_memory baseline 