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(具体@度娘)。    

时间: 2024-07-30 12:57:05

NDK开发之引用(局部引用,全局引用,虚全局引用)的相关文章

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

转载请注明出处:http://blog.csdn.net/xyang81/article/details/44657385 ????这篇文章比较偏理论,详细介绍了在编写本地代码时三种引用的使用场景和注意事项.可能看起来有点枯燥,但引用是在JNI中最容易出错的一个点,如果使用不当,容易使程序造成内存溢出,程序崩溃等现象.所以讲得比较细,有些地方看起来可能比较啰嗦,还请轻啪!下一篇文章会写一个在Android由于JNI引用使用不当,造成局部引用表溢出而导致程序闪退的案例,请关注! ????做Java

《Java开发手册》学习进程之对象引用与接口引用间的赋值和强制类型转换问题

对象引用之间: 子类引用可以赋值给父类引用. 父类引用需要在强制转换之后才能赋值给子类引用. 对于对象引用的强制转换,只要被转换的引用类型与转换后的目标类型之间是派生或被派生的关系,就可以通过编译.如果没有这些关系而去强制转换,则编译报错. 即使编译通过,如果被转换的引用指向的对象类型与转换后的目标类型之间不相符或不兼容(即被转换的引用指向的对象类型不能转换为除自身或者自身父类的其他类型,同下),则运行出错. 接口引用之间: 子接口引用可以赋值给父接口引用. 父接口引用需要在强制类型转换之后才能

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

在Android开发中,基本上很少有用到软引用或弱引用,这两个东东若用的很好,对自己开发的代码质量的提高有很大的帮助.若用的不好,会坑了自己.所以,在还没有真正的去了解它们之前,还是慎用比较好. 下面将通过两个Demo来结识软引用和弱引用在开发中的运用. 一. WeakReference:防止内存泄漏,要保证内存被虚拟机回收. 下面以一个时间更新的Demo来说明弱引用的运用. 1. main.xml文件代码如下: [html] view plaincopy <?xml version="1

左值、左值引用、右值、右值引用

1.左值和右值的概念 左值是可以放在赋值号左边可以被赋值的值:左值必须要在内存中有实体:         右值当在赋值号右边取出值赋给其他变量的值:右值可以在内存也可以在CPU寄存器.         一个对象被用作右值时,使用的是它的内容(值),被当作左值时,使用的是它的地址. 2.引用 引用是C++语法做的优化,引用的本质还是靠指针来实现的.引用相当于变量的别名. 引用可以改变指针的指向,还可以改变指针所指向的值. 引用的基本规则: 声明引用的时候必须初始化,且一旦绑定,不可把引用绑定到其他

引用专题(续)=》 常量引用

常引用分两点: 1 使用变量初始化const引用 const int &a = b 2 使用字面量常量初始化const引用  const int &m = 10: #include <iostream> using namespace std; void main() {     //普通引用     int a = 10;     int &b = a;     printf("b"%d\n",b);               //常引

OC 中的强引用(strong referene)和弱引用( weak reference)

关于oc中的强弱引用,有需要的朋友可以参考下. 强引用和弱引用的广义区别 强引用也就是通常所讲的引用,其存亡直接决定了所指对象的存亡.如果不存在指向一个对象的引用,并且此对象不再显示列表中,则此对象会被从内存中释放.弱引用除了不决定对象的存亡外,其他与强引用相同.即使一个对象被持有无数个若引用,只要没有强引用指向他,那么其还是会被清除 简单来说,strong等同retain(ARC之前),而weak和assign,weak比assign多了一个功能,当对象消失后自动把指针变成nil. __wea

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

首先注意一点,在VB6中缺省参数传递的方式是:引用传递,而在VB.NET中缺省参数传递的方式是:值传递. 然后我们看下面VB6中的引用传递与VB.NET中的引用传递的对比. VB6中的引用传递 Private Sub CommandButton1_Click() ChangeName CommandButton1.caption End Sub Private Sub ChangeName(caption As String) caption = "NameHasBeenChanged!&quo

shared_ptr造成的循环引用&amp;&amp;解决方法和原理(弱引用&amp;&amp;强引用)

 弱用指针的方式解决shared_ptr造成的循环引用防止内存泄漏! <***>循环引用就是由于智能指针shared_ptr造成的,下面就是shared_ptr的使用造成循环引用的图解: <****>举个例子来说下shared_ptr造成的循环引用: (选题背景双向链表) <span style="font-size:18px;">#include<memory> #include<iostream> using namesp

java 谈谈引用(强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference))

简单谈谈引用(摘自java虚拟机第二版 ) 署名:wander   一.四种引用 在JDK 1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference).软引用(Soft Reference).弱引用(Weak Reference).虚引用(Phantom Reference)4种,这4种引用强度依次逐渐减弱. 二.引用介绍及回收时机 1.强引用 >>> 就是指在程序代码之中普遍存在的,类似"Object obj=new Object()&q