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++的差异。建议尽量减少或避免使用本地代码。
想要了解更多内容?