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开发之引用(局部引用,全局引用,虚全局引用)的更多相关文章

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

    转自:http://blog.csdn.net/xyang81/article/details/44657385   这篇文章比较偏理论,详细介绍了在编写本地代码时三种引用的使用场景和注意事项.可能看 ...

  2. android WeakReference(弱引用 防止内存泄漏)与SoftReference(软引用 实现缓存机制(cache))

    在Android开发中,基本上很少有用到软引用或弱引用,这两个东东若用的很好,对自己开发的代码质量的提高有很大的帮助.若用的不好,会坑了自己.所以,在还没有真正的去了解它们之前,还是慎用比较好. 下面 ...

  3. Item 25: 对右值引用使用std::move,对universal引用则使用std::forward

    本文翻译自<effective modern C++>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 右值引用只能绑定那些有资格被move的对象上去.如 ...

  4. 强引用(StrongReference)、弱引用(WeakReference)、软引用(SoftReference)、虚引用(PhantomReference)

    1.强引用(StrongReference) 强引用是使用最普遍的引用.如果一个对象具有强引用,那垃圾回收器绝不会回收它.如下: Object o=new Object(); // 强引用 当内存空间 ...

  5. VB6中的引用传递 与 VB.NET中的引用传递的区别

    首先注意一点,在VB6中缺省参数传递的方式是:引用传递,而在VB.NET中缺省参数传递的方式是:值传递. 然后我们看下面VB6中的引用传递与VB.NET中的引用传递的对比. VB6中的引用传递 Pri ...

  6. 引用问题rayshop.common总是提醒重复引用问题

    引用问题rayshop.common总是提醒重复引用问题 解决办法:在插件组里的所有项目引用,使用不能复制属性

  7. java传递是引用的拷贝,既不是引用本身,更不是对象

    java传递是引用的拷贝,既不是引用本身,更不是对象 2008-09-16 04:27:56|  分类: Java SE|举报|字号 订阅     下载LOFTER客户端     1. 简单类型是按值 ...

  8. [整理] webpack+vuecli打包生成资源相对引用路径与背景图片的正确引用

    webpack+vuecli打包生成资源相对引用路径与背景图片的正确引用 https://www.cnblogs.com/moqiutao/p/7496718.html

  9. Android SDK NDK开发总结

    描述:http://talent.baidu.com/external/baidu/index.html#/jobDetail/2/1237247043 android studio实现Jni(C/C ...

随机推荐

  1. pow的小事不简单

    http://acm.hdu.edu.cn/showproblem.php?pid=5878 #include<stdio.h> #include<iostream> #inc ...

  2. C/C++中的指针数组和数组指针

    1. 指针数组 定义:int *p[n],由于[]的优先级高于*,p和[]结合成一个数组,该数组的元素存储的是int类型的指针,由于数组内容是指针,因此p+1的步长是sizeof(int*),在32位 ...

  3. String 及其数组的相关问题

    由其他类型转String一般用三种方法 方法1:采用 Object.toString()方法 请看下面的例子: Object object = getObject(); System.out.prin ...

  4. git 代码组织

    在20145306CSAPP2E文件夹下建立相应的文件夹: src:存放源代码文件 include: 存放头文件 bin:存放编译后的目标文件.可执行文件等 lib:存放项目所需的静态库.动态(共享) ...

  5. skiplist

    §1 Skip List 介绍 Skip List是一种随机化的数据结构,基于并联的链表,其效率可比拟于二叉查找树(对于大多数操作需要O(log n)平均时间).基本上, 跳跃列表是对有序的链表增加上 ...

  6. mgo-后续测试(指定字段,获取id)

    测试完mgo中的DBRef后,想接着测试指定字段的显示,才发现原来采用框架编码,很多问题被隐藏了起来: 1.显示指定字段: 之前在使用mgo时一直是查询全部字段,在mongo终端环境写为如下格式: & ...

  7. weed-fs 压力测试

    阅读<Weed-FS/杂草文件系统 小文件存储集群 安装 使用 测试>中提到weedfs的负载压力不是很好,在看过代码后进行了相应测试,未发现负载压力有何问题.   weedfs mast ...

  8. delphi Inc函数和Dec函数的用法

    inc自增函数 .inc(i,n)://i,n:integer;n为自增量 相当于i:=i+n: .inc(i)://i:integer; 相当于i:=i+;   dec自减函数 .dec(i,n): ...

  9. 更好更快更高效解析JSON说明

    现在来一个实例解析类,直接就把解析JSON到QVariant去了.唯一不足的是没有搞错误处理,具体方法也请各位自行参考json-c的发行文档,这样比较方便叙述,STL或者Boost我都没有认真接触过, ...

  10. sqlserver无法连接

    以下是我的检查信息及结果:1.telnet 192.168.1.100 1433 通过  telnet 116.3.15.198 1433 不通,提示“……无法打开连接,连接失败”的错误.2.通过端口 ...