jni入门

数据类型

基本数据类型

基本数据类型

引用数据类型

引用数据类型

方法签名

方法签名

局部引用和全局引用

局部引用

局部引用在栈中,栈弹出会被释放。但是如果在栈中有大量的引用,还是需要手动管理吧,避免溢出,比如下面这种情况

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

   if ( NULL != myObj )
   {
      /* we know myObj is a valid local ref, so use it */
      jclass myClazz = (*env)->GetObjectClass(env, myObj);

      /* uses of myObj and myClazz, etc. but no new local refs */

      /* Without the following calls, we would leak */
      (*env)->DeleteLocalRef( env, myObj ); 
      (*env)->DeleteLocalRef( env, myClazz );
   }

} /* end while */

全局引用

全局引用需要手动管理
NewGlobalRef
DeleteGlobalRef

必须通过调用NewGlobalRef或NewWeakGlobalRef从另一个有效的 JNI 引用(全局或本地)获取有效的 JNI 全局引用。
不用时要手动释放。

弱全局引用

不作为gc的根对象,随时可能比回收,使用是要进行测试是否已经被垃圾回收
IsSameObject 与NULL比较

JNI 引用管理

JNI 引用管理有一套独立于平台的规则

这些规则是:

  1. JNI 引用仅在附加到 JVM 的线程中有效。
  2. 必须获得本机代码中的有效 JNI 本地引用:
    - 作为本机代码的参数
    - 作为调用 JNI 函数的返回值
  3. 必须通过调用NewGlobalRef或NewWeakGlobalRef从另一个有效的 JNI 引用(全局或本地)获取有效的 JNI 全局引用。
  4. 空值引用始终有效,并且可以用来代替任何 JNI 引用(全局或本地)。
  5. JNI 本地引用仅在创建它们的线程中有效,并且仅在它们的创建帧保留在堆栈上时才保持有效。

**
注意:
使用空值覆盖本机存储中的本地或全局引用不会从根集中删除该引用。使用适当的 Delete*Ref JNI 函数从根集中删除引用。
如果存在未决的异常,许多 JNI 函数(例如FindClass和NewObject )将返回空值。将这些调用的返回值与空值进行比较在语义上等同于调用 JNI ExceptionCheck函数。有关更多详细信息,请参阅 JNI 规范。
无论在什么情况下,在创建框架返回后都不得使用 JNI 本地引用。在任何进程静态存储中存储 JNI 本地引用都是危险的。
**

copy和pin

copy

GC 可能随时决定它需要压缩垃圾收集堆。压缩涉及将对象从一个地址物理移动到另一个地址。这些对象可能被 JNI 本地或全局引用引用。为了允许安全地进行压缩,JNI 引用不是指向堆的直接指针。至少有一层间接将本机代码与对象移动隔离开来。

如果本机方法需要获得对对象内部的直接可寻址性,则情况会更加复杂。在需要对大型原始数组进行快速共享访问的情况下,直接寻址或固定堆的要求是典型的。一个示例可能包括屏幕缓冲区。在这些情况下,可以使用 JNI 临界区,这对程序员提出了额外的要求,如这些函数的 JNI 描述中所指定的。有关详细信息,请参阅 JNI 规范。

GetPrimitiveArrayCritical返回 Java™ 数组的直接堆地址,在调用相应的ReleasePrimitiveArrayCritical之前禁用垃圾收集。
GetStringCritical返回 java.lang.String 实例的直接堆地址,在调用ReleaseStringCritical之前禁用垃圾收集。
所有其他 Get ArrayElements 接口返回一个不受压缩影响的副本。

使用平衡垃圾回收策略时,*Critical形式的调用可能不会返回指向堆的直接指针,这反映在 isCopy 标志中。这种行为是由于较大数组的内部表示,其中数据可能不是连续的。通常,存储小于堆的 1/1000 的数组作为直接指针返回。

使用 isCopy 标志

JNI Get 函数指定一个按引用传递的输出参数 (jboolean *isCopy),它允许调用者确定给定的 JNI 调用是返回副本的地址还是堆中固定对象的地址。

Get 和 Release 函数成对出现:

GetStringChars和ReleaseStringChars
GetStringCritical和ReleaseStringCritical
GetStringUTFChars和ReleaseStringUTFChars
获取ArrayElements并 释放ArrayElements
GetPrimitiveArrayCritical和 ReleasePrimitiveArrayCritical
如果将非空地址作为 isCopy 参数传递,则如果返回的地址是数组元素副本的地址,则 JNI 函数将该地址处的 jboolean 值设置为 JNI_TRUE,如果地址直接指向固定对象,则 JNI_FALSE在堆中。

除了关键函数,J9 VM 总是返回一个副本。复制减轻了 GC 的负担,因为固定的对象不能被压缩并且使碎片整理变得复杂。

为避免泄漏,您必须:

使用 Get Region 和 Set Region 函数自行管理复制内存。
当不再需要副本时,通过调用相应的 Release 函数来确保释放由 Get 函数生成的副本。

使用模式(mode)标志

当您调用 Release ArrayElements 时,最后一个参数是模式标志。模式标志用于避免在处理复制数组时不必要地复制到 Java 堆。如果您正在使用已固定的数组,则忽略模式标志。

对于每个 Get 调用,您必须调用 Release 一次,无论 isCopy 参数的值如何。此步骤是必要的,因为调用 Release 会删除可能会阻止垃圾收集的 JNI 本地引用。

模式标志的可能设置是:
0
更新 Java 堆上的数据。释放副本使用的空间。
JNI_COMMIT
更新 Java 堆上的数据。不要释放副本使用的空间。
JNI_ABORT
不要更新 Java 堆上的数据。释放副本使用的空间。
‘0’ 模式标志是 Release 调用的最安全选择。无论数据的副本是否更改,堆都随着副本更新,并且没有泄漏。

为避免必须复制回未更改的副本,请使用 JNI_ABORT 模式值。如果您更改返回的数组,请在使用 JNI_ABORT 模式值“回滚”更改之前检查 isCopy 标志。此步骤是必要的,因为固定 JVM 使堆处于与复制 JVM 不同的状态。

使用 isCopy 和 mode 标志的通用方法
这是使用 isCopy 和 mode 标志的通用方法。它适用于所有 JVM,并确保提交更改并且不会发生泄漏。

要以通用方式使用标志,请确保:
不要使用 isCopy 标志。传入 null 或 0。
始终将模式标志设置为零。
这些标志的复杂使用仅用于优化。如果你使用通用的方式,你仍然必须考虑同步。请参阅同步。

同步

当您通过 Get ArrayElements 调用获取数组元素时,您必须考虑同步。

无论数据是否固定,访问数据都涉及两个实体:
声明和使用数据实体的 Java™ 代码
通过 JNI 访问数据的本机代码
这两个实体可能是单独的线程,在这种情况下会发生争用。

在复制 JNI 实现中考虑以下场景:

  1. Java 程序创建一个大数组并用数据部分填充它。
  2. Java 程序调用本机写入函数将数据写入套接字。
  3. 实现write()调用 GetByteArrayElements 的 JNI 本机。
  4. GetByteArrayElements 将数组的内容复制到缓冲区中,并将其返回给本机。
  5. JNI 本机开始将缓冲区中的区域写入套接字。
  6. 当线程忙于写入时,另一个线程(Java 或本机)运行并将更多数据复制到数组中(正在写入的区域之外)。
  7. JNI 本机完成将区域写入套接字。
  8. JNI 本地调用 ReleaseByteArrayElements 模式为 0,以指示它已完成对数组的操作。
  9. VM 看到模式 0,将缓冲区的全部内容复制回数组,并覆盖第二个线程写入的数据。
    在这个特定场景中,代码与固定 JVM 一起工作。因为每个线程只写入自己的数据位并且忽略模式标志,所以不会发生争用。此场景是未严格按照规范编写的代码如何与一个 JVM 实现而不是另一个实现一起工作的另一个示例。虽然这种情况涉及到数组元素的复制,但当两个线程同时访问固定数据时,它也可能会损坏。

请注意如何同步对数组元素的访问。您可以使用 JNI 接口访问 Java 数组和字符串的区域,以减少此类交互中的问题。在这种情况下,正在写入数据的线程会写入自己的区域。正在读取数据的线程只读取它自己的区域。此方法适用于每个 JNI 实现。

参考文章

ibm
csdn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值