NDK开发之引用(局部引用,全局引用,虚全局引用)
1、先引出我遇到的一个问题(我觉得先写问题,这样印象更深刻一点):
Android Java 层在调用本地 jni 代码的时候, 会维护一个局部引用表(最大长度是 512), 一般 jni 函数调用结束后, jvm 会释放这个引用, 如果是简单的函数不注意这些问题,让他自己释放,基本是没有什么问题, 但是如果函数里面有循环的操作的话,那么程序就会有崩溃的隐患, 比如 之前我在项目里面就有在一个 jni 方法里面,将 native 层的一个队列封装成 Java 层的 List,然后返回给 Java 层使用, 这样的话,不可避免的在本地方法里面有循环操作,而自己恰恰有些小细节没有注意, 忘了删除一些局部应用,开始测少量数据的时候一点问题没有, 后来数据规模大了, 直接就崩了,还好 log 日志里面打印出了崩溃日志,然后知道是代码里面是有引用没有释放, 具体日志是这样的:

我的代码里面大概是这样的:
jobject collect(JNIEnv *env)
{ for (.., .. , ..)
{ jobject obj = doSomething(env, clazz); //删除引用 env->DeleteLocalRef(obj); env->DeleteLocalRef(clazz); //如果clazz是不变的话, 那么这段代码可以不写, FindClass毕竟还是耗性能的 } } jobject doSomething(JNIEnv *env, jclass clazz)
{ return env->NewObject(clazz, env->NewStringUTF(str.c_str())); }
上面只是我代码的大概结构, 意思懂就行了哈, 这里我其实在主调用函数里面是删除了局部引用的, 但是在调用方法的时候使用了doSomeThing()这样的类似方法, 可以看出,doSomeThing()函数返回的时候生成了一个局部引用jstring, 但是没有释放,这就为后面的崩溃埋下了隐患, 惆怅了一会,好歹让我找到原因了,将doSomething()改成下面的写法后, 问题得到完满的解决,
jobject doSomething(JNIEnv *env)
{
jstring test = env->NewSringUTF(str.c_str());
jobject ret = env->NewObject(clazz, test);
env->DeleteLocalRef(test);
return ret;
}
2、 jni引用总结:
引用在 java 程序设计里面扮演了一个很重要的角色, 虚拟机通过追踪引用来管理类实例的生命周期, 虽然虚拟机不能管理 native 代码, 但是 JNI 提供了一系列的方法允许本地代码精确的管理类的引用和生命周期, JNI 支持三种类型的引用, 局部引用 (local references), 全局引用 (global references), 虚全局引用 (weak global references); 嘛, 总之,引用是个很重要的东西, java gc 程序是根据类的引用是否为 0 来决定是否回收类的, 如果本地代码持有 java 对象的引用而不释放的话, 那么 gc 是无法正常回收对象的, 存在内存泄漏的风险。
.局部引用 (Local Reference)
大部分的 JNI 方法返回的都是局部引用, 注意,局部引用不可以缓存下来,局部引用的生命周期局限在本地方法里面, 一旦本地代码返回了,那么本地引用就会被释放掉, 举个栗子: env->FindClass() 返回一个局部引用, 当本地代码返回的时候它将会自动释放掉, 当然本地代码也可以通过 DeleteLocalRef 方法精确的控制局部引用的释放;
jclass clazz;
clazz = env->FindClass("java/lang/String”);
.全局引用 (Global Reference)
全局引用可以保留有效的引用直到本地方法精确的释放它(比如作为全局变量,局部静态变量用局部引用是不行的,但是全局引用可以), 全局引用可以通过 NewGlobalRef 方法初始化局部引用得到(貌似只有这个方法吧,噗~)
jclass localClazz;
jclass globalClazz;
...
localClazz = env->FindClass("java/lang/String");
globalClazz = env->NewGlobalRef(localClazz);
...
env->DeleteLocalRef(localClazz);
辣么问题来了, 就像局部引用可以手动释放一样, 全局引用可以手动释放么? 嘿嘿,一样通过 env->DeleteGlobalRef() 来释放全局引用啦。
.虚全局引用
还有一个全局引用是虚全局引用, 和全局引用的作用一样,但是虚全局引用不会阻止 gc 回收对象, 而全局引用由于持有对象的引用,导致 gc 无法回收。 通过 NewWeakGlobalRef 来新建虚函数引用。
jclass weakGlobalClazz; weakGlobalClazz = env->NewWeakGlobalRef(localClazz);
喜欢思考的同学这时候会发现一个问题, 由于虚全局引用不能阻止 gc 回收对象, 那么我们肿么知道对象已经被回收了呢,吖,不用担心, jni 已经提供了方法给窝们了
if (JNI_FALSE == env->IsSameObject(weakGlobalClazz, NULL)){
//object is still live and can be used.
}else {
// object is garbage collected and cannot be used
}
删除虚全局引用的方法类似: env->DeleteWeakGlobalRef(weakGlobalClazz); 这个方法任何时候都可以使用。 除非我们精确的释放,全局引用都会保留有效的引用,可以在其他的本地方法里面调用。虚全局引用也一样,不过要注意是否 gc 已经回收了对象了。
注意:
虚拟机支持在多线程下运行本地代码, 我们在开发本地代码的时候需要记住以下几个地方:
1. 局部引用直到本地方法执行期间都是有效的, 局部引用不可以多线程共享, 只有全局引用可以多线程共享。
2. JNIEnv 接口指针通过被调用的本地方法得到的在相同的线程的其他方法里面都是有效的, 但是它不能够缓存下来给其他线程使用,解决方法也有,通过 JVM 获得 JNIEnv (具体@度娘)。
NDK开发之引用(局部引用,全局引用,虚全局引用)的更多相关文章
- JNI/NDK开发指南(十)——JNI局部引用、全局引用和弱全局引用
转自:http://blog.csdn.net/xyang81/article/details/44657385 这篇文章比较偏理论,详细介绍了在编写本地代码时三种引用的使用场景和注意事项.可能看 ...
- android WeakReference(弱引用 防止内存泄漏)与SoftReference(软引用 实现缓存机制(cache))
在Android开发中,基本上很少有用到软引用或弱引用,这两个东东若用的很好,对自己开发的代码质量的提高有很大的帮助.若用的不好,会坑了自己.所以,在还没有真正的去了解它们之前,还是慎用比较好. 下面 ...
- Item 25: 对右值引用使用std::move,对universal引用则使用std::forward
本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 右值引用只能绑定那些有资格被move的对象上去.如 ...
- 强引用(StrongReference)、弱引用(WeakReference)、软引用(SoftReference)、虚引用(PhantomReference)
1.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.如下: Object o=new Object(); // 强引用 当内存空间 ...
- VB6中的引用传递 与 VB.NET中的引用传递的区别
首先注意一点,在VB6中缺省参数传递的方式是:引用传递,而在VB.NET中缺省参数传递的方式是:值传递. 然后我们看下面VB6中的引用传递与VB.NET中的引用传递的对比. VB6中的引用传递 Pri ...
- 引用问题rayshop.common总是提醒重复引用问题
引用问题rayshop.common总是提醒重复引用问题 解决办法:在插件组里的所有项目引用,使用不能复制属性
- java传递是引用的拷贝,既不是引用本身,更不是对象
java传递是引用的拷贝,既不是引用本身,更不是对象 2008-09-16 04:27:56| 分类: Java SE|举报|字号 订阅 下载LOFTER客户端 1. 简单类型是按值 ...
- [整理] webpack+vuecli打包生成资源相对引用路径与背景图片的正确引用
webpack+vuecli打包生成资源相对引用路径与背景图片的正确引用 https://www.cnblogs.com/moqiutao/p/7496718.html
- Android SDK NDK开发总结
描述:http://talent.baidu.com/external/baidu/index.html#/jobDetail/2/1237247043 android studio实现Jni(C/C ...
随机推荐
- Android 另类方法监听软键盘的弹出收起事件
http://www.cnblogs.com/csonezp/p/5065624.html 最近做的项目碰到个问题,a界面是fragment+recyclerview,b界面带个edittext,并且 ...
- 使用C#访问SQLLite
1.SQLLite如何集成在C#中 2.相关C#与SQLLite资源,及说明 3.简单示例
- Redis 新特性---pipeline(管道)
转载自http://weipengfei.blog.51cto.com/1511707/1215042 Redis本身是一个cs模式的tcp server, client可以通过一个socket连续发 ...
- 用ping命令来模拟traceroute的功能
ping -n 1 -r 9 qq.com 正在 Ping qq.com [163.177.65.160] 具有 32 字节的数据:来自 163.177.65.160 的回复: 字节=32 时间=11 ...
- Vs打包工程 错误: Unable to update the dependencies of the project (转)
Setup Project 错误: Unable to update the dependencies of the project 在VS2010中编译包含安装工程的解决方案提示错误:Unable ...
- opencv透视变换
关于透视投影的几何知识,以及求解方法,可以参考 http://media.cs.tsinghua.edu.cn/~ahz/digitalimageprocess/chapter06/chapt06_a ...
- 如何用js刷新aspxgridviw
//写在js中 ASPxGridView1.Refresh();
- .Net的Excel 导出 格式设置
添加引用:Microsoft Excel 11.0 Object Library ; 添加:using Microsoft.Office.Interop.Excel; 一.打开Exce ...
- cs11_c++_lab2
Matrix.hh class Matrix { int row; int col; int *p; public: Matrix(); Matrix(int x,int y); ~Matrix(); ...
- 用odbc连接oracle问题
如果用11g的客户端,然后通过odbc(远程连接)连接10g的oracle,会出现监听程序无法启动(ORA-12541: TNS: 无监听程序) 此时需要在客户端目录中D:\instantclient ...