Java JNI 编程:全局引用的实际使用

Java JNI Programming : Actual usage of the Global References

在我的 i cached the JNIEnv* between JNI Calls. And from the 中,我开始知道它无效,这导致我学习了 JNI 本地和全局引用。我做了一些测试程序来理解它。从测试程序中我无法理解全局引用的使用。因为本地引用本身在多个 JNI 调用之间工作正常。我有 3 个来自测试程序的问题

  1. 很想知道原因,本地引用是如何被缓存并正常工作的。
  2. ArrayList 大小的更改有效,但 String 对象无效
  3. 了解缓存的 JNIEnv 如何工作的原因,尽管它无效(在我之前的问题中)。

下面给出测试代码。

jni代码:

jclass cls1, cls2, cls3;
jmethodID mth1, mth2, mth3;
jstring str1, str2;
jobject obj1, obj2, obj3;

JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnFindClass
(JNIEnv *env, jobject obj) {
    if (cls1 == NULL || str1 == NULL || obj1 == NULL) {
        cout << "Initializing new string object" << endl;
        cls1 = env->FindClass("java/lang/String");
        mth1 = env->GetMethodID(cls1, "<init>", "(Ljava/lang/String;)V");
        str1 = env->NewStringUTF("Apple");
        obj1 = env->NewObject(cls1, mth1, str1);

        mth1 = env->GetMethodID(cls1, "length", "()I");
        long i = (long) env->CallIntMethod(obj1, mth1);
        cout << "Length = " << i << endl;
        //env->DeleteLocalRef(cls1);
        //env->DeleteLocalRef(str1);
        //env->DeleteLocalRef(obj1);
    } else {
        cout << "String Object already initialized" << endl;
        mth1 = env->GetMethodID(cls1, "length", "()I");
        long i = (long) env->CallIntMethod(obj1, mth1);
        cout << "Length = " << i << endl;
    }
}

JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnGetExistingObject__Ljava_lang_String_2
(JNIEnv *env, jobject obj, jstring str) {
    if (cls2 == NULL || obj2 == NULL) {
        cout << "Initializing from existing string object" << endl;
        cls2 = env->GetObjectClass(str);
        obj2 = (jobject) env->NewLocalRef(str);
        mth2 = env->GetMethodID(cls2, "length", "()I");
        long i = (long) env->CallIntMethod(obj2, mth2);
        cout << "Length = " << i << endl;
        //env->DeleteLocalRef(cls2);
        //env->DeleteLocalRef(obj3);
    } else {
        cout << "Object already initialized" << endl;
        mth2 = env->GetMethodID(cls2, "length", "()I");
        long i = (long) env->CallIntMethod(obj2, mth2);
        cout << "Length = " << i << endl;
    }
}

JNIEXPORT void JNICALL Java_c_wrapperforjavaclass_clsNative_fnGetExistingObject__Ljava_util_ArrayList_2
(JNIEnv *env, jobject obj, jobject lst) {
if (cls3 == NULL || obj3 == NULL) {
        cout << "Initializing from existing ArrayList object" << endl;
        cls3 = env->GetObjectClass(lst);
        obj3 = (jobject) env->NewLocalRef(lst);
        mth3 = env->GetMethodID(cls3, "size", "()I");
        long i = (long) env->CallIntMethod(obj3, mth3);
        cout << "Size = " << i << endl;
        //env->DeleteLocalRef(cls3);
        //env->DeleteLocalRef(obj3);
    } else {
        cout << "Object already initialized" << endl;
        mth3 = env->GetMethodID(cls3, "size", "()I");
        long i = (long) env->CallIntMethod(obj3, mth3);
        cout << "Length = " << i << endl;
    }
}

调用Java代码(测试代码):

a.fnFindClass();
a.fnFindClass();

String str = new String("Android OS");
a.fnGetExistingObject(str);
//Increasing the size
str = new String("Will modified string length get effect");
a.fnGetExistingObject(str);

ArrayList<Integer> al = new ArrayList<>();
al.add(1);al.add(2);al.add(3);
a.fnGetExistingObject(al);
al.add(4);al.add(5); //Increasing the size
a.fnGetExistingObject(al);

测试结果:

Initializing new string object
Length = 5
String Object already initialized
Length = 5

Initializing from existing string object
Length = 10
Object already initialized
Length = 10

Initializing from existing ArrayList object
Size = 3
Object already initialized
Length = 5

提前致谢。

全局引用阻止垃圾收集器删除相关对象。该对象也可能不会被收集只是因为另一个 java 对象引用了它,或者因为垃圾收集器在两次调用之间的短时间内没有 运行。你不应该依赖这个,而是保留对你以后可能需要的任何东西的全局引用。

可以调整数组列表的大小。字符串是不可变的,总是需要为子字符串或追加等函数创建一个新字符串。

我认为保留 JNIEnv * 的问题与线程安全有关。 C++ 代码无法知道两个不同的调用不是来自两个不同的线程。每个环境需要附加到不同的线程。