jni中的本地引用和全局引用

转载 2012年03月26日 22:49:29
JNI支持三种类型的java对象引用:局部引用(local reference)、全局引用(global reference)以及弱全局引用(weak global reference)。三种类型的引用具有不同的生命周期,另外垃圾回收器对这三种对象引用的管理方式也不同。创建局部引用的本地方法返回后(注意:这里是指返回到java方法),局部引用将变成无效。而全局引用以及弱全局引用在本地方法返回后,仍然有效。垃圾回收器无法回收本地方法创建的局部引用和全局引用,但可以回收本地方法创建的弱全局引用。为了编写正确而且内存安全的java本地方法,很有必要对这三种类型的对象引用做些总结。
    一 局部引用的注意事项 
    大多数jni方法创建的对象引用都属于局部引用(local reference),如FindClass方法、NewObject等。局部引用不能作为静态变量,也不能作为全局变量。如下一段程序就是错误的: 
    Jstring MyNewString(JNIEnv *env, jchar *chars, jint len) 
    { 
        static jclass stringClass = NULL; 
        jmethodID cid; 
        jcharArray elemArr; 
        jstring result; 
        if (stringClass == NULL) { 
            stringClass = (*env)->FindClass(env, "java/lang/String"); 
            if (stringClass == NULL) { 
                return NULL; /* exception thrown */ 
            }
         } 
        /*直接使用stringClass是危险的,因为stringClass是局部引用。 */ 
        cid = (*env)->GetMethodID(env, stringClass, "<init>", "([C)V"); 
        ... 
        elemArr = (*env)->NewCharArray(env, len); 
        ... 
        result = (*env)->NewObject(env, stringClass, cid, elemArr);
        (*env)->DeleteLocalRef(env, elemArr); 
        return result; 
    } 
    虚拟机中,每一个方法都对应着执行栈(interpreter stack)中的一个帧(frame)。Java方法对应着Java帧(Java Frame),本地方法对应着转变帧(Transition Frame)。局部引用是保存在帧栈(stack Of frame)中,当方法返回时帧就会从执行栈中弹出。因此,帧栈中所有的引用就全部变成无效。在本地方法返回前,局部引用的内存是不能被垃圾回收器回收的。如果一个本地方法中存在循环且循环体中会创建局部引用,那么很有可能导致outOfMemeroy异常。为了保证内存安全,程序员应该在循环体中调用DeleteLocalRef来释放局部引用。
    不同的虚拟机实现垃圾回收的算法并不相同,传统上有两种方法:引用计数法、触及法。引用计数法无法检测出循环引用,现在已经很少使用。触及法的总体思路是:首先创建一个根集合,该集合中的对象是肯定处于活跃态的;然后从根集合开始做遍历,所有可被遍历到的对象都被认为是处于活跃态的;最后,非活跃态的对象被回收。帧栈中的引用会在垃圾回收的第一步中被加入根集合,所以在执行栈将局部引用所属帧弹出前,局部引用是肯定不会被垃圾回收器回收的。 
    二 全局引用的注意事项 
    只有一个jni方法可以创建全局引用:NewGlobalRef。下面给出了一段创建全局引用的程序: 
    Jstring MyNewString(JNIEnv *env, jchar *chars, jint len) 
    { 
        static jclass stringClass = NULL; 
        ... 
        if (stringClass == NULL) {
             //创建一个局部引用 
             jclass localRefCls=(*env)->FindClass(env, "java/lang/String"); 
            if (localRefCls == NULL) { 
                return NULL; /* exception thrown */ 
            }
             /* 创建一个全局引用 */ 
            stringClass = (*env)->NewGlobalRef(env, localRefCls);
            
 /* 局部引用localRefCls不再有效,删除局部引用localRefCls*/ 
            (*env)->DeleteLocalRef(env, localRefCls); 
            if (stringClass == NULL) { 
                return NULL; /* out of memory exception thrown */
             }
         }
         ... 
    } 
    与局部引用类似的是,在垃圾回收的第一个过程中全局引用也会被加入到根集合中。而且即使创建全局引用的帧从执行栈中弹出后,全局引用仍然有效。所以,释放全局引用的工作必须由程序员完成。调用DeleteGlobalRef来释放全局引用。
    三 弱全局引用的注意事项 
    创建弱全局引用的接口是:NewGlobalWeakRef。释放弱全局引用的接口是:DeleteWeakGlobalRef(《jni手册》中对这个接口的前后描述不一致,我是对比了phoneME虚拟机的)。在本地方法返回后,弱全局引用仍然有效。另外,在垃圾回收期运行的第一步中,弱全局对象不会被加入根集合。因此,弱全局引用可以被垃圾回收器回收。下面给出了一段使用弱全局引用的程序: 
    JNIEXPORT void JNICALL Java_mypkg_MyCls_f(JNIEnv *env, jobject self) { 
        static jclass myCls2 = NULL; 
        if (myCls2 == NULL) { 
            jclass myCls2Local = (*env)->FindClass(env, "mypkg/MyCls2");
            if (myCls2Local == NULL) {
                return; /* can’t find class */ 
            } 
            myCls2 = NewWeakGlobalRef(env, myCls2Local);
            if (myCls2 == NULL) { 
                return; /* out of memory */
            }
        } 
        ... 
        /* use myCls2 */ 
    } 
    对于上面的例程,本地方法f属于类java.mypkg.MyCls。可以认为引用myCls2的生命周期与类java.mypkg.MyCls相同。 

参考资料:
1. 《jni程序员手册》
2. 《深入Java虚拟机》
3. 《phoneME-advance-mr2》源码

相关文章推荐

JNI局部变量和全局变量引用

1.局部变量引用 定义在本地方法内的局部变量,会随着方法调用完return后,局部变量也会随着被释放。 所以,不要在本地方法中定义static变量来存储数据!举个栗子: 像上面这样子是不行滴!...
  • fwt336
  • fwt336
  • 2017年03月08日 11:04
  • 983

JNI学习笔记——局部和全局引用

JNI将实例、数组类型暴露为不透明的引用。native代码从不会直接检查一个不透明的引用指针的上下文,而是通过使用JNI函数来访问由不透明的引用所指向的数据结构。因为只处理不透明的引用,这样就不需要担...

java-基础-JNI本地栈

jni全称是Java Native Interface是在JAVA和Native层(包括但不限于C/C++)相互调用的接口规范。 JNI在JAVA1.1中正式推出,在JAVA1.2版本中加入...

JNI/NDK开发指南(十)——JNI局部引用、全局引用和弱全局引用

转载请注明出处:http://blog.csdn.net/xyang81/article/details/44657385    这篇文章比较偏理论,详细介绍了在编写本地代码时三种引用的使用场景和注意...
  • xyang81
  • xyang81
  • 2015年04月04日 16:28
  • 7976

使用JNIEnv全局变量调用FindClass()等函数发生crash

********** Crash dump: ********** Build fingerprint: 'samsung/gt58ltezc/gt58ltechn:5.0.2/LRX22G/T355...
  • XXOOYC
  • XXOOYC
  • 2015年09月07日 14:01
  • 4724

android 自定义view实现数字进度条

android 自定义view实现数字进度条

android 自定义view实现类似圆盘抽奖的效果

android 自定义view实现类似圆盘抽奖的效果

Android NDK学习笔记13-JNI的局部和全局引用

局部引用    大多数JNI函数返回局部引用。局部引用不能在后续的调用中被缓存及重用,主要是因为它们的使用期限仅限于原生方法,一旦原生函数返回,局部引用即被释放。例如,使用FindClass函数返回一...

JNI全局引用与JFrame.dispose()方法

用 jProfiler 分析 Java swing 程序中的内存泄漏问题时, 我发现内存中 JFrame 实例的数量一直在增加。 各个 frame 被打开(opened),然后被关闭(closed)。...

JNI官方文档翻译5-局部和全局引用

时间过得好快,一转眼写到了第五章,JNI对于许多java程序员来说一般是很陌生的,因为,要想使用JNI必须使用C,然而很多Java程序员都不会C,其实实际上不是这样的。项目中应该是分工协作才是,JNI...
  • mtaxot
  • mtaxot
  • 2016年05月16日 17:30
  • 907
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:jni中的本地引用和全局引用
举报原因:
原因补充:

(最多只允许输入30个字)