JNI基础学习笔记--引用管理

13 篇文章 0 订阅
5 篇文章 0 订阅

1. 为什么要使用引用?

由于Java程序运行在虚拟机中的这个特点,在Java中创建的对象、定义的变量和方法,内部对象的数据结构是怎么定义的,只有JVM自己知道。如果我们在C/C++中想要访问Java中对象的属性和方法时,是不能够直接操作JVM内部Java对象的数据结构的。想要在C/C++中正确的访问Java的数据结构,JVM就必须有一套规则来约束C/C++与Java互相访问的机制,所以才有了JNI规范,JNI规范定义了一系列接口,任何实现了这套JNI接口的Java虚拟机,C/C++就可以通过调用这一系列接口来间接的访问Java中的数据结构。常用JNI接口有:GetStringUTFChars(从Java虚拟机中获取一个字符串)、ReleaseStringUTFChars(释放从JVM中获取字符串所分配的内存空间)、NewStringUTF、GetArrayLength、GetFieldID、GetMethodID、FindClass等。

所以, Java将类、对象、String、数组是通过引用暴露给Native层。

2. 引用分类
在JNI规范中定义了三种引用:局部引用(Local Reference)、全局引用(Global Reference)、弱全局引用(Weak Global Reference)区别如下表:

这里写图片描述

局部和全局引用

局部引用限于其创建的堆栈帧和线程,并且在其创建的堆栈帧返回时会自动删除。全局引用允许本机代码将局部引用提升为与 JVM 相连的任何线程中的本机代码可以使用的格式。

全局引用和内存泄漏

全局引用不会自动删除,因此程序员必须处理内存管理。每个全局引用都会为引用目标建立根,并且使用户可以访问整个子树。因此,必须释放创建的每个全局引用以防止发生内存泄露。
全局引用中的泄露最终会导致发生内存不足异常。 这些错误可能难于解决,尤其是在您不执行 JNI 异常处理时。请参阅处理异常。
为了提供 JNI 全局引用功能并提供某些对引用目标的自动垃圾回收功能,JNI 提供了两个函数:

NewWeakGlobalRef
DeleteWeakGlobalRef

这些函数为 JNI 提供弱引用访问。

局部引用和内存泄漏

局部引用范围之外的自动垃圾回收可在大多数情况下防止发生内存泄露。 在本机线程返回到 Java(本机方法)或断开与 JVM 的连接 (Invocation API) 时,将执行此类自动垃圾回收。如果不执行自动垃圾回收,那么可能会出现局部引用内存泄露。如果本机方法未返回至 JVM,或者如果使用 Invocation API 的程序未断开与 JVM 的连接,那么可能会发生内存泄漏。
请考虑以下示例中的代码,其中本机代码将在循环中创建新的局部引用:

while ( <condition> )
{
   jobject myObj = (*env)->NewObject( env, clz, mid, NULL );

   if ( NULL != myObj )
   {
      /* 我们知道 myObj 是有效的本地引用,所以使用它 */
      jclass myClazz = (*env)->GetObjectClass(env, myObj);

      /* 使用 myObj 和 myClazz 等,但没有新的本地引用 */

      /* 不执行以下调用,将导致漏出 */
      (*env)->DeleteLocalRef( env, myObj ); 
      (*env)->DeleteLocalRef( env, myClazz );
   }
} /* end while */

尽管新的局部引用会覆盖循环中的 myObj 和 myClazz 变量,但是每个局部引用仍保留在根集中。必须使用 DeleteLocalRef 调用来显式除去这些引用。 如果不使用 DeleteLocalRef 调用,那么局部引用将发生泄漏,直至线程返回至 Java 或者断开与 JVM 的连接。

JNI 弱全局引用

弱全局引用是特殊类型的全局引用。可在任何线程中使用,并且可在本机函数调用之间使用,但不能用作 GC 根。如果弱全局引用所引用的对象在其他位置无强引用,那么 GC 可随时处理该对象。

使用弱全局引用时必须谨慎。如果对弱全局引用所引用的对象执行垃圾回收,那么该引用将变为空引用。空引用仅在与 JNI 函数的子集一起使用时才安全。要测试是否已回收弱全局引用,请使用 IsSameObject JNI 函数将弱全局引用与空值进行比较。

即使您已测试过弱全局引用不是空引用,使用该引用调用大多数 JNI 函数也不安全,这是因为弱全局引用在经过测试后,甚至在 JNI 调用函数期间都可能变为空引用。而弱全局引用在使用前应始终被提升为强引用。您可以使用 NewLocalRef 或 NewGlobalRef JNI 函数来提升弱全局引用。

弱全局引用占用内存,并且在不再需要时必须使用 DeleteWeakGlobalRef JNI 函数来释放。释放弱全局引用失败会导致内存缓慢泄露,最终导致发生内存不足异常。

有关使用 JNI 全局弱引用的信息和警告,请参阅 JNI 规范。

JNI 引用管理

提供了一组独立于平台的规则来管理 JNI 引用
这些规则包括:
1、JNI 引用仅在与 JVM 相连的线程中才有效。
2、必须在本机代码中获取作为以下项的有效 JNI 局部引用:
a、本机代码的参数
b、调用 JNI 函数的返回值
3、必须通过调用 NewGlobalRef 或 NewWeakGlobalRef,从另一个有效的 JNI 引用(全局或局部)获取有效的 JNI 全局引用。
4、空值引用始终是有效的,并且可用于代替任何 JNI 引用(全局或局部)。
5、JNI 局部引用仅在创建它们的线程中才有效,并且仅在它们创建的帧仍然位于堆栈上时才有效。

参考:
http://blog.csdn.net/xyang81/article/details/44657385

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值