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. Eclipse中FindBugs插件的应用

    在以前的一个开发现场里,经常会收到客户的代码review指摘. 觉得有点神奇是,给客户的文件是编译后的*.class打成war包,客户那边却能指摘出代码中的缺陷bug,而且精确到代码的某一行. 通过* ...

  2. Android Gradle 技巧之一: Build Variant 相关

    Build Variant android gradle 插件,允许对最终的包以多个维度进行组合. BuildVariant = ProductFlavor x BuildType 两个维度 最常见的 ...

  3. c语言实现二维数组排序,一个4*5的数组,要求每行都进行升序排列,并求出每行的平均值。

    #include<stdio.h>#define N 5#define M 4void main(){ int x,i,j,k,t,a[M][N]; float sum=0.0; floa ...

  4. svn 提交失败

    刚刚使用SVN 提交代码时提示失败. svn: Commit failed (details follow):svn: Can't open file '/home/svn/project/db/tx ...

  5. delphi 调用c#dll

    public interface iProduct { string Buy(); } [ClassInterface(ClassInterfaceType.None)] public class P ...

  6. osmdroid启程

    osmdroid一个牛逼的开源地图引擎,从今天开始好好研究一下~

  7. 黑盒测试与白盒测试(Black box Testing)

    黑盒测试和白盒测试的优缺点 类别 优点 缺点 黑盒测试 不需要了解软件代码 从用户角度出发 无法保证代码内各个路径被覆盖到 白盒测试 强制测试开发工程师关注代码的具体实现 揭露隐藏在代码中的Bug 是 ...

  8. WinCE6.0多国语言软键盘

    N久以前写过一篇<WinCE下自定义的大软键盘>,这个自定义软键盘就是为RM905a+项目来做的.RM905a+的系统分辨率是640*480,WinCE原生键盘小的太小,大的又太大.所以就 ...

  9. ORacle初级题

    一. 选择(每题1分,共15分) 1.在linux系统中,可以通过以下命令查看内核版本(). * A.who B.hostname C.uname -r D.release 2.登入linux系统后, ...

  10. 使用CodeSmith快速生成映射文件和映射类

    一 CodeSmith简介 本文以表自动生成NHibernate的映射文件和映射类的实例来说明一下本软件的使用方法. CodeSmith是一种基于模板的代码生成工具,其使用类似于ASP.NET的语法来 ...