JNI 引用到底是什么在 JNI 里你会看到这些类型jobject jclass jstring jarray jbyteArray jthrowable它们看起来像 C 指针但你不能把它们当普通指针理解。更准确地说它们是 Java 对象在 native 层的引用句柄。native 代码不能直接操作 Java 对象内存只能通过 JNIEnv 提供的接口间接访问。比如jstring nameenv-NewStringUTF(hello);constchar*charsenv-GetStringUTFChars(name,nullptr);jstring name只是一个对java中string类型的引用要转换成c的string得GetStringUTFCharsLocal Reference本地引用LocalRef 是最常见的 JNI 引用。可以说你在native层看到的东西大部分都是LocalRef例如externCJNIEXPORTvoidJNICALLJava_com_xxx_NativeBridge_nativeFoo(JNIEnv*env,jobject thiz){jstring strenv-NewStringUTF(hello);jclass clazzenv-FindClass(com/xxx/Foo);jstring strenv-NewStringUTF(hello);jobject objenv-CallObjectMethod(thiz,methodId);}这里面的str、clazz、str、obj甚至thiz、env都是LocalRef它的生命周期是当前 native 方法调用期间有效。native 方法返回后LocalRef 通常会被 JVM 自动释放。LocalRef 的两个典型错误错误 1把 LocalRef 保存到全局变量staticjobject gCallbacknullptr;externCJNIEXPORTvoidJNICALLJava_com_xxx_NativeBridge_nativeInit(JNIEnv*env,jobject thiz,jobject callback){gCallbackcallback;// 错}callback 是 LocalRef。nativeInit 返回后这个引用可能失效。后面再用 gCallback 回调 Java可能触发 JNI DETECTED ERROR甚至 SIGABRT。正确做法是gCallbackenv-NewGlobalRef(callback);错误 2循环里创建大量 LocalRef 不释放for(inti0;i100000;i){jstring strenv-NewStringUTF(hello);// 没有 DeleteLocalRef}虽然 LocalRef 会在 native 方法返回时统一释放但如果一个 native 方法内部循环创建太多 LocalRef可能在返回前就把 local reference table 撑爆。最好是env-DeleteLocalRef(str);手动释放一下Global Reference全局引用如果 native 层要长期保存 Java 对象就必须用 GlobalRef。上已经展示过写法了GlobalRef 的特点可以跨 native 方法调用保存。可以跨线程使用。会阻止 Java 对象被 GC。必须手动 DeleteGlobalRef。重点是第 3 点。如果你一直不释放那么这个 Java callback 对象就一直被 native 持有GC 不会回收它。如果 callback 持有 Activity就可能造成 Activity 泄漏。Weak Global Reference弱全局引用WeakGlobalRef 是弱引用版本的 GlobalRef。创建方式jweak gWeakCallbackenv-NewWeakGlobalRef(callback);它的特点是可以跨调用、跨线程保存。不阻止 Java 对象被 GC。使用前必须判断对象是否还活着。