JNI的三种引用

局部引用
通过NewLocalRef和各种JNI接口创建(FindClass、NewObject、GetObjectClass和NewCharArray等)。会阻止GC回收所引用的对象。函数返回后,局部引用所引用的对象(如果Java层没有对返回的局部引用使用)会被JVM自动释放,或调用DeleteLocalRef释放。(*env)->DeleteLocalRef(env,local_ref)

jclass cls_string = (*env)->FindClass(env, "java/lang/String");
jcharArray charArr = (*env)->NewCharArray(env, len);
jstring str_obj = (*env)->NewObject(env, cls_string, cid_string, elemArray);
jstring str_obj_local_ref = (*env)->NewLocalRef(env,str_obj);   // 通过NewLocalRef函数创建
...

如果使用静态变量存储局部引用,则在方法返回或手动释放后,该静态变量会成为野指针。

局部引用虽然会在方法返回(返回到java环境)后自动释放内存,但依然需要手动释放,原因如下:

1、JNI会将创建的局部引用都存储在一个局部引用表中,该局部引用表的最大数量是512。当一个本地方法中,创建了大量的局部引用,会导致局部引用表溢出。
2、局部引用会阻止所引用对象的GC回收,在不使用时,因尽可能的优先释放,避免占用内存。

如下代码依然会导致局部引用表溢出:

#include <jni.h>
#include <string>

jstring NewStringUTF(JNIEnv *env, const char *str) {
    // 方法入栈和出栈后应该会释放,但实际没有释放
    return env->NewStringUTF(str);
}

extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_ndk_ndkdemo_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    jint i = 0;
    std::string hello = "Hello from C++";
    for (i = 0; i < 1000000; i++)
        jstring str = NewStringUTF(env, hello.c_str());

    return NULL;
}
实际上,每当线程从Java环境切换到本地代码上下文时(J2N),JVM会分配一块内存,创建一个局部引用表,这个表用来存放本次本地方法执行中创建的所有的局部引用。每当在本地代码中引用到一个Java对象时,JVM就会在这个表中创建一个 局部引用。比如,实例一中我们调用NewStringUTF()在Java堆中创建一个String对象后,在局部引用表中就会相应新增一个局部引用。

依赖关系:局部变量 -> 局部引用表中局部引用 -> Java对象

当函数方法出栈,但并没有切换到Java环境时,仅是对局部变量进行清除,不会操作局部引用。

全局引用
调用NewGlobalRef基于局部引用创建,会阻GC回收所引用的对象。可以跨方法、跨线程使用。JVM不会自动释放,必须调用DeleteGlobalRef手动释放

static jclass g_cls_string;
void TestFunc(JNIEnv* env, jobject obj) {
    jclass cls_string = (*env)->FindClass(env, "java/lang/String");
    g_cls_string = (*env)->NewGlobalRef(env,cls_string);

    // 释放全局引用

    (*env)->NewGlobalRef(env, g_cls_string);
}

弱全局引用
调用NewWeakGlobalRef基于局部引用或全局引用创建,不会阻止GC回收所引用的对象,可以跨方法、跨线程使用。引用不会自动释放,在JVM认为应该回收它的时候(比如内存紧张的时候)进行回收而被释放。或调用DeleteWeakGlobalRef手动释放。(*env)->DeleteWeakGlobalRef(env,g_cls_string)

static jclass g_cls_string;
void TestFunc(JNIEnv* env, jobject obj) {
    jclass cls_string = (*env)->FindClass(env, "java/lang/String");
    g_cls_string = (*env)->NewWeakGlobalRef(env,cls_string);
}

弱全局引用可通过(*env)->IsSameObject(env, obj, NULL)判断是否被释放,如果被回收,则返回JNI_TRUE,否则返回JNI_FALSE
————————————————
版权声明:本文为CSDN博主「Alex_MaHao」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lisdye2/article/details/123631769

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值