扫码阅读
手机扫码阅读

JNI:全局引用&局部引用&弱全局引用

82 2024-07-21

我们非常重视原创文章,为尊重知识产权并避免潜在的版权问题,我们在此提供文章的摘要供您初步了解。如果您想要查阅更为详尽的内容,访问作者的公众号页面获取完整文章。

查看原文:JNI:全局引用&局部引用&弱全局引用
文章来源:
BUG弄潮儿
扫码关注公众号
摘要:Java JNI引用类型及缓存策略

引用类型

在Java虚拟机与本地C/C++代码交互时产生的引用分为三种:全局引用、局部引用和弱全局引用。这些引用的存在防止Java对象被垃圾回收器回收。

1. 局部引用

局部引用通常通过JNI返回,并仅在创建它的native函数中有效。它们在函数返回时自动释放,但可以使用DeleteLocalRef函数手动释放,尤其是在处理大对象或循环中创建局部引用时,以便及时被垃圾回收。局部引用不能被保存为C++全局变量或静态局部变量。

2. 全局引用

全局引用跨线程和多个native函数有效,但需要手动释放。它们通过调用NewGlobalRef函数创建,通过ReleaseGlobalRef函数释放。

3. 弱全局引用

弱全局引用在多个本地代码有效,可以跨线程使用,且不会阻止引用的Java对象被回收。使用NewWeakGlobalRef和ReleaseWeakGlobalRef来创建和释放。

引用操作函数

Java提供了一系列JNI函数来创建和操作这些引用,如NewGlobalRef、NewLocalRef、NewWeakGlobalRef、DeleteGlobalRef、DeleteLocalRef以及IsSameObject,后者可以检查弱全局引用指向的Java对象是否已被回收。

缓存jfieldID / jmethodID

获取jfieldID和jmethodID的操作相对开销较大,因此在开发中推荐缓存这些ID,有两种缓存策略:

5.1 在使用时缓存

在native代码中使用静态局部变量保存查询过的ID,但要注意多线程并发访问导致的问题。

JNIEXPORT void JNICALL Java_Test_native( JNIEnv* env, jobject ojb) {
static jfieldID fieldID_str = NULL;
  jclass clazz = env->GetObjectClass( obj );
  if(fieldID_str == NULL){
      fieldID_str = env->GetFieldID(clazz, "strField", "Ljava/lang/String");
  }
  //TODO Other codes
}

5.2 在Java类初始化时缓存

在Java类加载时,通过调用本地代码初始化所有ID,并保存为C/C++全局变量,以避免重复查询。

public class TestNative {
  static {
      initNativeIDs();
  }
  static native void initNativeIDs();
  //TODO Other codes
}
// C/C++ global variables
jfieldID g_propInt_id = 0;
jfieldID g_propStr_id = 0;
JNIEXPORT void JNICALL Java_TestNative_initNativeIDs( JNIEnv* env, jobject clazz){
  g_propInt_id = env->GetFieldID(clazz, "propInt", "I");
  g_propStr_id = env->GetFieldID(clazz, "propStr", "Ljava/lang/String;");
}
JNIEXPORT void JNICALL Java_TestNative_otherNative( JNIEnv* env, jobject obj){
  // TODO get field with g_propInt_id/g_propStr_id
}

总结

本文介绍了JNI中的三种引用类型、JNI操作引用的函数以及缓存jfieldID/jmethodID的两种策略。

回顾

使用JNI导致Java应用丧失跨平台能力,需要小心处理Java强类型与C/C++的差异。建议尽量减少或避免使用本地代码。

想要了解更多内容?

查看原文:JNI:全局引用&局部引用&弱全局引用
文章来源:
BUG弄潮儿
扫码关注公众号