Android NDK 开发(四)JNI 中局部引用、全局引用和弱全局引用

前言

做过Java的朋友都知道,内存管理这一块是完全透明的,new一个类的实例时,只知道创建完这个类的实例后,会返回这个实例的一个引用,然后拿着这个引用去访问它的成员了(属性、方法),完全不用管JVM内部怎么实现的,如何为新建的对象申请内存,使用完之后如何释放内存,只需要知道有个垃圾回收器在处理这些事情就行了,然而,从Java虚拟机创建的对象传到C/C++代码时会产生引用,根据Java的垃圾回收机制,只要有引用存在就不回触发该引用所指向Java对象的垃圾回收;这些引用在JNI 中分为3种:全局引用(Global Reference)、局部引用 (Local Reference)、弱全局引用 (Week Global Reference) since JDK1.2。


正文

三种引用的区别

1、全局引用

全局引用可以跨方法、跨线程使用,直到被开发者显式释放。类似局部引用,一个全局引用在被释放前保证引用对象不被GC回收。和局部引用不同的是,没有那么多函数能够创建全局引用。能创建全局引用的函数只有 NewGlobalRef。以下例子说明了如何使用一个全局引用。

java native方法:

    public native void createGlobalRef();

    public native String getGlobalRef();

    public native void deleteGlobalRef();

jni实现:

//全局引用
//共享(可以跨多个线程),手动控制内存使用
jstring global_str;

//创建
JNIEXPORT void JNICALL Java_com_study_jni_JniTest_createGlobalRef(JNIEnv *env, jobject jobj){
    jstring obj = (*env)->NewStringUTF(env, "jni development is powerful!");
    global_str = (*env)->NewGlobalRef(env, obj);
}

//获得
JNIEXPORT jstring JNICALL Java_com_study_jni_JniTest_getGlobalRef(JNIEnv *env, jobject jobj){
    return global_str;
}

//释放
JNIEXPORT void JNICALL Java_com_study_jni_JniTest_deleteGlobalRef(JNIEnv *env, jobject jobj){
    (*env)->DeleteGlobalRef(env, global_str);
}

2、局部引用

一个局部引用仅在创建它的native函数及该函数调用的函数中有效。在一个native函数执行期间创建的所有局部引用将在该函数返回时被释放,创建了大量的局部引用,占用了太多的内存,而且这些局部引用跟后面的操作没有关联性。

//模拟:循环创建数组
JNIEXPORT void JNICALL Java_com_study_jni_JniTest_localRef(JNIEnv *env, jobject jobj){
    int i = 0;
    for (; i < 5; i++){
        //创建Date对象
        jclass cls = (*env)->FindClass(env, "java/util/Date");
        jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
        jobject obj = (*env)->NewObject(env, cls, constructor_mid);
        //此处省略一百行代码...

        //不在使用jobject对象了
        //通知垃圾回收器回收这些对象
        //释放局部引用
        (*env)->DeleteLocalRef(env, obj);
        //此处省略一百行代码...
    }
}

上面代码中,省略了和我们无关紧要的代码,通过FindClass返回一个对java.util.Date对象的局部引用。


3、弱全局引用

节省内存,在内存不足时可以是释放所引用的对象,可以引用一个不常用的对象,如果为NULL,临时创建,弱全局引用使用NewGlobalWeakRef创建,使用DeleteGlobalWeakRef释放。下面简称弱引用。与全局引用类似,弱引用可以跨方法、线程使用。但与全局引用很重要不同的一点是,弱引用不会阻止GC回收它引用的对象,所以在使用时需要多加小心,它所引用的对象可能是不存在的或者已经被回收。

1.创建弱全局引用

用NewWeakGlobalRef函数对弱全局引用进行初始化,例如:

jclass weakGlobalcls
weakGlobalcls = (*env)->NewWeakGlobalRef(env,localclazz);
2.引用的比较

给定两个引用(不管是全局、局部还是弱全局引用),我们只需要调用IsSameObject来判断它们两个是否指向相同的对象。例如:(*env)->IsSameObject(env, obj1, obj2)
如果obj1和obj2指向相同的对象,则返回JNI_TRUE(或者1),否则返回JNI_FALSE(或者0)。有一个特殊的引用需要注意:NULL,JNI中的NULL引用指向JVM中的null对象。如果obj是一个局部或全局引用,使用(*env)->IsSameObject(env, obj, NULL) 或者 obj == NULL 来判断obj是否指向一个null对象即可。但需要注意的是,IsSameObject用于弱全局引用与NULL比较时,返回值的意义是不同于局部引用和全局引用的。比如:

if(JNI_FALSE == (*env)->IsSameObject(env,weakGlobalcls,NULL)){
//TODO 对象未被回收,可以使用
}else{
//TODO 对象被垃圾回收器回收,不能使用
}

以上就是学习这jni 的三种引用的简单使用,相关的知识并没有深入详细的说明!O(∩_∩)O~~

本文由博主辛苦整理下来的笔记;
希望大家能够指点或提出宝贵意见,共同学习,谢谢!
转载请注明出处:http://xuhaoblog.com/ndk/jni-4.html
欢迎关注我的社交网站~
个人博客:xuhaoblog.com
新浪微博:http://weibo.com/xuxiho
github:https://github.com/git-xuhao