JNI返回Java对象引发GC异常导致应用崩溃问题咨询
问题核心原因
Java的finalize机制存在一个关键特性:一旦对象被GC标记为可回收并加入finalizer队列,即使后续对象重新变为可达,finalizer线程仍会执行其finalize方法。结合你的场景:
- GC提前启动,标记了缓存中的Java对象为可回收,将其加入finalizer队列,但此时finalizer线程还没来得及处理;
- Java侧调用C++的
getObj时,通过env->NewLocalRef(weak_obj)让对象重新可达,但此时finalizer队列已经锁定该对象待处理; - 后续finalizer线程执行对象的
finalize方法(对应C++侧的资源销毁逻辑),导致对象内部状态失效,此时Java侧仍持有引用,调用methodB就会访问已销毁的资源,触发崩溃。
可行解决方案
方案1:给对象添加跳过finalize的标记
在Java对象中增加一个开关,当从C++缓存中取回对象时,标记它不需要执行finalize逻辑,避免资源被销毁:
Java代码:
public class TargetObject { private boolean skipFinalize = false; @Override protected void finalize() throws Throwable { if (!skipFinalize) { // 原有的C++侧销毁逻辑调用 nativeDestroy(); } super.finalize(); } // 供C++调用,标记对象无需执行finalize public void markReused() { skipFinalize = true; } }
C++代码:
jobject getObj(JNIEnv* env, jweak weakObj) { jobject localRef = env->NewLocalRef(weakObj); if (localRef != nullptr) { jclass objCls = env->GetObjectClass(localRef); jmethodID markMethod = env->GetMethodID(objCls, "markReused", "()V"); env->CallVoidMethod(localRef, markMethod); env->DeleteLocalRef(objCls); } return localRef; }
方案2:替换jweak为强引用+显式销毁
放弃依赖jweak和finalize,改用全局强引用缓存,由Java侧显式通知C释放资源,彻底避免GC和finalizer的冲突:
C侧缓存逻辑:
#include <unordered_map> #include <string> std::unordered_map<std::string, jobject> globalObjCache; // 缓存Java对象,创建全局强引用 void cacheObject(JNIEnv* env, const std::string& key, jobject obj) { auto iter = globalObjCache.find(key); if (iter != globalObjCache.end()) { env->DeleteGlobalRef(iter->second); } globalObjCache[key] = env->NewGlobalRef(obj); } // 获取对象,返回本地引用 jobject getObject(JNIEnv* env, const std::string& key) { auto iter = globalObjCache.find(key); if (iter != globalObjCache.end()) { return env->NewLocalRef(iter->second); } return nullptr; } // 显式销毁对象,释放全局引用 void destroyObject(JNIEnv* env, const std::string& key) { auto iter = globalObjCache.find(key); if (iter != globalObjCache.end()) { env->DeleteGlobalRef(iter->second); globalObjCache.erase(iter); } }
Java侧调用逻辑:
public class UIManager { // 使用对象后主动调用销毁 public void finishUsingObject(String key) { nativeDestroyObject(key); } }
方案3:临时提升为全局引用直到使用完毕
在C侧取回对象时,临时创建全局引用,确保对象不会被GC再次标记,直到Java侧使用完毕再释放:
C代码:
#include <unordered_map> #include <string> std::unordered_map<std::string, jobject> tempGlobalRefs; jobject getObj(JNIEnv* env, const std::string& key, jweak weakObj) { jobject localRef = env->NewLocalRef(weakObj); if (localRef != nullptr) { // 创建临时全局引用,防止GC回收 jobject globalRef = env->NewGlobalRef(localRef); tempGlobalRefs[key] = globalRef; } return localRef; } // Java侧使用完毕后调用,释放临时全局引用 void releaseTempRef(JNIEnv* env, const std::string& key) { auto iter = tempGlobalRefs.find(key); if (iter != tempGlobalRefs.end()) { env->DeleteGlobalRef(iter->second); tempGlobalRefs.erase(iter); } }
Java侧使用示例:
public void processObject(String key) { TargetObject obj = nativeGetObj(key); try { obj.methodA(); // 业务逻辑执行 obj.methodB(); } finally { // 确保释放临时引用 nativeReleaseTempRef(key); } }
内容的提问来源于stack exchange,提问作者comrade_bender
相关产品推荐
相关产品推荐

