6.JIN:全局引用&局部引用&弱全局引用

2023-04-15  本文已影响0人  BUG弄潮儿

从Java虚拟机创建的对象传到本地 C/C++ 代码时就会产生引用。根据Java的垃圾回收机制,只要有引用存在就不会触发该引用指向的Java对象的垃圾回收。这些引用在 JNI 中分为三种

  1. 全局引用 (Global Reference)
  2. 局部引用 (Local Reference)
  3. 弱全局引用 (Weak Global Reference), JDK 1.2 引入

1. 局部引用

例如,使用NewObject就会返回创建出来的实例的局部引用。局部引用只在该native函数中有效,所有在该函数中产生的局部引用,都会在函数返回的时候自动释放(freed)。也可以使用DeleteLocalRef函数进行手动释放该引用。

实际上局部引用存在,就会防止其指向的对象被垃圾回收。尤其是当一个局部引用指向一个很庞大的对象,或是在一个循环中生成了局部应用;最好的做法就是在使用完该对象后,或在该循环尾部把这个引用释放掉,以确保在垃圾回收器被触发的时候被回收。

2. 全局引用

3. 弱全局引用

弱全局应用是 JDK 1.2 新出来的功能,与全局引用相似,创建跟释放都需要由编程人员来进行操作。这种引用与全局引用一样可以在多个本地代码有效,也可以跨越多线程有效;不一样的是,这种引用将不会阻止垃圾回收器回收这个引用所指向的对象。

使用 NewWeakGlobalRef 跟 ReleaseWeakGlobalRef 来产生和释放应用。

4. 关于引用的一些函数

jobject NewGlabalRef(jobject obj);
jobject NewLocalRef(jobject obj);
jobject NewWeakGlobalRef(jobject obj);

void DeleteGlobalRef(jobject obj);
void DeleteLocalRef(jobject obj);
jboolean IsSameObject(jobject obj1, jobject obj2);

IsSameObject 函数对于弱引用全局应用还有一个特别的功能,把NULL传入要比较的对象中,就能够判断弱全局引用所指向的Java对象是否被回收。

5. 缓存jfieldID / jmethodID

获取 jfieldID与jmethodID 的时候会通过该属性/方法名称加上签名来查询相应的 jfieldID/jmethodID。这种查询相对来说开销较大。在开发中可以将这些 FieldID/MethodID 缓存起来,这样就只需要查询一次,以后就使用缓存起来的 FieldID/MethodID。

  1. 在使用时缓存 (Caching at the Point of Use)
  2. 在Java类初始化时缓存 (Caching at the Defining Class's Inititalizer)

5.1 在使用时缓存

在native 代码中使用static局部变量来保存已经查询过的jfieldID/jmethodID ,这样就不会在每次的函数调用时查询,而只要一次查询成功后就保存起来了。

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
}

不过这种情况下,就不得不考虑多线程同时调用此函数时可能导致同时查询的并发问题,不过这种情况是无害的,因为查询同一个属性或者方法的ID,通常返回的值是一样的。

5.2 在Java类初始化时缓存

java代码

public class TestNative {
    
    static {
        initNativeIDs();
    }
    
    static native void initNativeIDs();
    
    int propInt =0;
    
    String propStr = "";
    
    public native void otherNative();
    
    //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
}

6. 总结

7. 回顾

《The Java Native Interface Programmer's Guide and Specification》

《JNI++ User Guide》

上一篇 下一篇

猜你喜欢

热点阅读