最近开发过程中遇到了JNI的Reference相关问题,了解到Local Reference和Global Reference的相关知识点,整理如下:
背景:项目需求,在Native C/C++层调用上层Android Camera Java接口,把所有的操作包括Camera都沉到Native层去实现。但在JNI调试过程中遇到了android JNI ERROR (app bug): accessed stale local reference的报错。
现象:在Native层创建Java的Camera对象,其对象的指针保存到本地,函数返回到Java层,之后再进入Native层,想通过Native层的Camera对象指针调用相应的方法,但是发现每次都是重新调用Java对象方法后报错。
分析:在Native层创建的Java对象,对象创建后会有一个局部引用指向该对象,当从Native环境返回到Java环境,该局部引用失效,此对象就没有引用计数,Java的内存回收机制会自动回收该对象,第二次再进入Native层访问其之前保存的地址时就会报错。
解决:使用全局引用始终持有该对象的引用使其不被自动回收,请看下面的知识点。
Local Reference
局部引用,看如下精简代码:
env->NewStringUTF("0");
在JNI中,每次调用NewObject方法创建一个新的对象都会返回一个对该对象的局部引用(Local Reference),该局部引用只在线程当前的Native环境中有效,返回到Java环境后该引用与对象之间的联系就会被断掉,引用失效,所以我们不能在Native方法中把局部引用缓存用于下一次调用时使用。
局部引用可以无限创建吗
如图:
局部引用表这里引入局部引用表的概念,每当线程从Java环境进入到Native环境后,JVM就会创建该线程Native环境的局部引用表,用来保存本次Native环境所创建的所有局部引用,每当Native中引用或者新创建一个Java对象,JVM就会局部引用表创建一个局部引用,局部引用表是有大小限制的,最大是512,如果超过限制会报OOM内存泄漏。
Q:那如何才能更好的避免由于局部引用过多造成Native环境中的OOM呢?
A:控制局部引用的生命周期,如果需要创建过多的局部引用,可以在Java对象的操作结束后,手动调用DeleteLocalRef函数删除局部引用,该局部引用就会在局部引用表中被移除,避免触发局部引用表的大小限制。
注意:局部引用不是我们平时所理解的代码中的局部变量,局部变量在当前生命周期(例如函数退出)结束后就会失效,而局部引用在函数退出后可能不会失效,它的生命周期是和整个Native上下文环境相关联,只有从Native环境返回到Java环境后局部引用才会失效。
Global Reference
全局引用,终于到了最上面讨论的问题了,因为局部引用在Native环境返回到Java环境后就会失效,导致下次进入Native环境后再次使用相对应的Java对象就会出错,所以可以使用全局引用来解决这个问题,全局引用可以始终与Java对象保持联系,使得此对象不会被JVM回收掉,见如下代码:
JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) {
jclass tmp = env->FindClass("com/example/company/MyClass");
jclass class = env->NewGlobalRef(tmp);
return JNI_VERSION_1_6;
}
这里需要注意,在不需要使用Java对象后尽量手动调用DeleteGlobalRef()函数来使得引用失效,避免对象始终存在,产生潜在的内存泄漏。
Weak Global Reference
虚全局引用与全局引用的区别在于该类型的引用可能随时被JVM回收掉,这里涉及到几个函数:
NewWeakGlobalRef();
DeleteWeakGlobalRef();
isSameObject();
在使用虚引用前需要通过isSameObject将其和NULL比较,如果返回TRUE返回true表示已经被JVM回收掉就不能使用了,这里有可能前一行代码判断还是可用,后一行代码时就被JVM回收,解决办法时通过NewLocalRef()获取虚全局引用,避免当时被JVM回收。
参考资料
https://www.cnblogs.com/zhongshujunqia/p/4638077.html?utm_source=tuicool&utm_medium=referral
https://www.cnblogs.com/younghome/p/4609044.html
https://stackoverflow.com/questions/14765776/jni-error-app-bug-accessed-stale-local-reference-0xbc00021-index-8-in-a-tabl
https://www.ibm.com/developerworks/cn/java/j-lo-jnileak/index.html
https://juejin.im/post/5c19bfa0f265da6133568545
网友评论