android jni deletelocalref,JNI 引用, DeleteLocalRef使用场景详解

局部引用:

JNI 函数内部创建的 jobject对象及其子类( jclass、 jstring、 jarray等) 对象都是局部引用,它们在 JNI 函数返回后无效;

一般情况下,我们应该依赖 JVM 去自动释放 JNI 局部引用;但下面两种情况必须手动调用 DeleteLocalRef()去释放:

(在循环体或回调函数中)创建大量 JNI 局部引用,即使它们并不会被同时使用,因为 JVM 需要足够的空间去跟踪所有的 JNI 引用,所以可能会造成内存溢出或者栈溢出;

如果对一个大的 Java 对象创建了 JNI 局部引用,也必须在使用完后手动释放该引用,否则 GC 迟迟无法回收该 Java 对象也会引发内存泄漏.

全局引用:

全局引用允许你持有一个 JNI 对象更长的时间,直到你手动销毁;但需要显式调用 NewGlobalRef()和 DeleteGlobalRef():

class MyPeer {

private:

jstring s;

public:

MyPeer(JNIEnv* env, jstring s) {

this->s = env->NewGlobalRef(s);

}

~MyPeer() {

env->DeleteGlobalRef(s);

s = NULL;

}

};

弱全局引用

弱全局引用类似 Java 中的弱引用,它允许对应的 Java 对象被 GC 回收;

类似地,创建和释放也是通过 NewWeakGlobalRef()和 DeleteWeakGlobalRef();

调用 IsSameObject(env, jobj, NULL)可以判断该弱全局引用指向的 Java 对象是否已被 GC 回收。

jobject 对象的引用值不唯一

同一个 jobject对象的不同引用可能拥有不同的值,比如同一 jobject对象每次调用 NewGlobalRef()可能返回不同的值;

要检查两个引用是否指向同一个 jobject对象,必须调用 IsSameObject(),而不要使用 ==去比较;

用于描述一个 jobject对象的 32 位值可能在方法多次调用后发生变化,而两个不同 jobject对象却可能在多次方法调用拥有相同的值,所以千万不能将 jobject对象的值当作 key 使用;

jmethodID 和 jfieldID:

在 JNI 层执行 Java 代码常用到 FindClass()、 GetMethodID()、 GetFieldID();

但只有第一个函数返回的 jclass属于 JNI (局部)引用对象,而 jmethodID和 jfieldID并不是,它们是指向内部 Runtime 数据结构的指针;

实际上这些 ID 是用于缓存的静态对象:第一次查找会做一次字符串比较,但后面再次调用就能直接读取而变得很快;

JVM 会保证这些 ID 是合法的,直到 Class被 unload;

所以, jmethodID和 jfieldID是不需要手动释放的,当然也不能作为 JNI 全局引用。

其他非 JNI 引用:

除了上面提到的 ID,类似 GetStringUTFChars()和 GetByteArrayElements()/ GetCharArrayElements()等函数返回的也是 Raw Data 指针,而非 JNI 引用;

在调用相对应的 ReleaseXXX()函数释放前,它们都是合法的;

批量操作 JNI 引用:

一般情况下要避免大量创建 JNI 局部引用,最好用完后立即释放(实际上目前的实现只预留了 16 个局部引用的空间);

如果确实需要大量操作 JNI 局部引用,要么调用 EnsureLocalCapacity()指定更多的空间,要么调用 PushLocalFrame()/ PopLocalFrame()批量分配/释放:

env->PushLocalFrame(128);

jobjectArray array = env->NewObjectArray(128, gMyClass, NULL);

for (int i = 0; i < 128; ++i) {

env->SetObjectArrayElement(array, i, newMyClass(i));

}

env->PopLocalFrame(array);

开启 CheckJNI 检查 JNI 引用问题:

Android 提供了一种叫做 CheckJNI 的模式用于检测常见的 JNI 错误,其中和 JNI 引用相关的错误有:

将 DeleteGlobalRef()/ DeleteLocalRef()用于错误的 JNI 引用类型;

jfieldID/ jmethodID为空或者类型不合法;

但是 CheckJNI 暂时还不能检测 JNI 局部引用的滥用问题,比如:存储了一个 JNI 局部引用,然后在 JNI 函数返回后继续使用。这种情况很显然应该使用 NewGlobalRef()创建全局 JNI 引用。

开启 CheckJNI 只需一行命令即可:

adb shell setprop debug.checkjni 1

参考:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值