局部引用:

JNI 函数内部创建的 jobject 对象及其子类( jclass 、 jstring 、 jarray 等) 对象都是局部引用,它们在 JNI 函数返回后无效;

一般情况下,我们应该依赖 JVM 去自动释放 JNI 局部引用;但下面两种情况必须手动调用 DeleteLocalRef() 去释放:

  • (在循环体或回调函数中)创建大量 JNI 局部引用,即使它们并不会被同时使用,因为 JVM 需要足够的空间去跟踪所有的 JNI 引用,所以可能会造成内存溢出或者栈溢出;

  • 如果对一个大的 Java 对象创建了 JNI 局部引用,也必须在使用完后手动释放该引用,否则 GC 迟迟无法回收该 Java 对象也会引发内存泄漏.

全局引用:

全局引用允许你持有一个 JNI 对象更长的时间,直到你手动销毁;但需要显式调用 NewGlobalRef() 和 DeleteGlobalRef() 

class MyPeer {
private:
jstring s;
public:
MyPeer(JNIEnv* env, jstring s) {
this->s = env->NewGlobalRef(s);
}
~MyPeer() {
env->DeleteGlobalRef(s);
s = NULL;
}
};

弱全局引用

弱全局引用类似 Java 中的弱引用,它允许对应的 Java 对象被 GC 回收;

类似地,创建和释放也是通过 NewWeakGlobalRef() 和 DeleteWeakGlobalRef() 

调用 IsSameObject(env, jobj, NULL) 可以判断该弱全局引用指向的 Java 对象是否已被 GC 回收。

jobject 对象的引用值不唯一

同一个 jobject 对象的不同引用可能拥有不同的值,比如同一 jobject 对象每次调用 NewGlobalRef() 可能返回不同的值;

要检查两个引用是否指向同一个 jobject 对象,必须调用 IsSameObject() ,而不要使用 == 去比较;

用于描述一个 jobject 对象的 32 位值可能在方法多次调用后发生变化,而两个不同 jobject 对象却可能在多次方法调用拥有相同的值,所以千万不能将 jobject 对象的值当作 key 使用;

jmethodID 和 jfieldID:

在 JNI 层执行 Java 代码常用到 FindClass() 、 GetMethodID() 、 GetFieldID() 

但只有第一个函数返回的 jclass 属于 JNI (局部)引用对象,而 jmethodID 和 jfieldID 并不是,它们是指向内部 Runtime 数据结构的指针;

实际上这些 ID 是用于缓存的静态对象:第一次查找会做一次字符串比较,但后面再次调用就能直接读取而变得很快;

JVM 会保证这些 ID 是合法的,直到 Class 被 unload;

所以, jmethodID 和 jfieldID 是不需要手动释放的,当然也不能作为 JNI 全局引用。

其他非 JNI 引用:

除了上面提到的 ID,类似 GetStringUTFChars() 和 GetByteArrayElements() GetCharArrayElements() 等函数返回的也是 Raw Data 指针,而非 JNI 引用;

在调用相对应的 ReleaseXXX() 函数释放前,它们都是合法的;

批量操作 JNI 引用:

一般情况下要避免大量创建 JNI 局部引用,最好用完后立即释放(实际上目前的实现只预留了 16 个局部引用的空间);

如果确实需要大量操作 JNI 局部引用,要么调用 EnsureLocalCapacity() 指定更多的空间,要么调用 PushLocalFrame() PopLocalFrame() 批量分配/释放:

env->PushLocalFrame(128);
jobjectArray array = env->NewObjectArray(128, gMyClass, NULL);
for (int i = 0; i < 128; ++i) {
env->SetObjectArrayElement(array, i, newMyClass(i));
}
env->PopLocalFrame(array);

开启 CheckJNI 检查 JNI 引用问题:

Android 提供了一种叫做 CheckJNI 的模式用于检测常见的 JNI 错误,其中和 JNI 引用相关的错误有:

  • 将 DeleteGlobalRef() DeleteLocalRef() 用于错误的 JNI 引用类型;

  • jfieldID jmethodID 为空或者类型不合法;

但是 CheckJNI 暂时还不能检测 JNI 局部引用的滥用问题,比如:存储了一个 JNI 局部引用,然后在 JNI 函数返回后继续使用。这种情况很显然应该使用 NewGlobalRef() 创建全局 JNI 引用。

开启 CheckJNI 只需一行命令即可:

adb shell setprop debug.checkjni 1

参考 

转自:http://ju.outofmemory.cn/entry/224516

JNI 引用问题梳理(转)的更多相关文章

  1. 第42篇-JNI引用的管理(1)

    在本地函数中会使用Java服务,这些服务都可以通过调用JNIEnv中封装的函数获取.我们在本地函数中可以访问所传入的引用类型参数,也可以通过JNI函数创建新的 Java 对象.这些 Java 对象显然 ...

  2. 第43篇-JNI引用的管理(2)

    之前我们已经介绍了JNIHandleBlock,但是没有具体介绍JNIHandleBlock中存储的句柄,这一篇我们将详细介绍对这些句柄的操作. JNI句柄分为两种,全局和局部对象引用: (1)大部分 ...

  3. Android Jni引用第三方库

    在jni下新建文件夹(jniLib)用来存放第三方so库: 将so拷贝到jniLib下,新建一个Android.mk文件: LOCAL_PATH:= $(call my-dir) include $( ...

  4. jni 字符串的梳理 2 字符串的处理操作

    我们实现下面的一个功能: 1.首先在java层传递一个字符串到c层,c层首先将jstring转换成char*类型,然后将两个字符串相加,然后再再将char*类型转换成jstring,在上层显示出来 我 ...

  5. jni 字符串的梳理

    1.实现的功能是java层传递一个字符串到c层2.c层首先将jstring类型转换成char*类型3.c层对字符串进行处理之后,将处理之后的char*类型转换成jstring类型返回给上层的 pack ...

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

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

  7. NDK学习笔记-JNI的引用

    JNI中的引用意在告知虚拟机何时回收一个JNI变量 JNI引用变量分为局部引用和全局引用 局部引用 局部引用,通过DeletLocalRef手动释放对象 原因 访问一个很大的Java对象,使用之后还用 ...

  8. 管中窥豹——从对象的生命周期梳理JVM内存结构、GC调优、类加载、AOP编程及性能监控

    如题,本文的宗旨既是透过对象的生命周期,来梳理JVM内存结构及GC相关知识,并辅以AOP及双亲委派机制原理,学习不仅仅是海绵式的吸收学习,还需要自己去分析why,加深对技术的理解和认知,祝大家早日走上 ...

  9. Java Native Interface 五 JNI里的多线程与JNI方法的注册

    本文是<The Java Native Interface Programmer's Guide and Specification>读书笔记 JNI里的多线程 在本地方法里写有关多线程的 ...

随机推荐

  1. Mac环境配置 - iOS开发人员 -待续

    Mac环境记录 Mac 相关 目录相关 显示: $ defaults write com.apple.finder AppleShowAllFiles -bool true 隐藏: $ default ...

  2. 制作高仿QQ的聊天系统(上)—— 布局文件 & 减少过度绘制

    由于没有自己的服务器,我就找了个能实现双方通信的SDK,这个SDK是友盟的用户反馈SDK.本系列的博文关注的不是网络通信,而是如何在网络通信机制已经做好的情况下,做出一个可用的聊天系统.其实,刚开始做 ...

  3. 根据ImageView的大小来压缩Bitmap,避免OOM

    Bitmap是引起OOM的罪魁祸首之一,当我们从网络上下载图片的时候无法知道网络图片的准确大小,所以为了节约内存,一般会在服务器上缓存一个缩略图,提升下载速度.除此之外,我们还可以在本地显示图片前将图 ...

  4. Gallery和自定义Adapter配合使用,实现图片预览

    Gallery是一个可以拖动的列表,正中对应的是选中的东西.他和spinner有共同的父类:AbsSpinner 属性: android:animationDuration="1000&qu ...

  5. Google SRE 读书笔记 扒一扒SRE用的那些工具

    写在前面 最近花了一点时间阅读了<SRE Goolge运维解密>这本书,对于书的内容大家可以看看豆瓣上的介绍.总体而言,这本书是首次比较系统的披露Google内部SRE运作的一些指导思想. ...

  6. NameError: name 'reduce' is not defined

    听说了大名鼎鼎的 reduce 函数,可以是执行的时候却显示这个. In [1]: reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) ---------------- ...

  7. protobuf 更新消息和扩展,包

    一.更新一个消息类型 如果一个已有的消息格式已无法满足新的需求--如,要在消息中添加一个额外的字段--但是同时旧版本写的代码仍然可用.不用担心!更新消息而不破坏已有代码是非常简单的.在更新时只要记住以 ...

  8. JAVA 是否会发生内存泄露(转)

    原文链接: JAVA 是否会发生内存泄露 几次面试,面试官都问到了这个问题,于是搜集了答案.总结出虽然java自身有垃圾回收机制,但是很多情况下还是发生内存泄露的. java导致内存泄露的原因很明确: ...

  9. 相声段子:How Are You

    /**************************************************************** File name : HowAreYou Author : 叶飞影 ...

  10. 随机蕨(Random Fern)

    最近在做 Zdenek Kalal 的 TLD 算法,其成果发表在CVPR 2010 上,文章的名字叫做 P-N Learning: Bootstrapping Binary Classifiers ...