Java JNI编程: Actual usage of the Global References

标签 java java-native-interface

在我的previous question我在 JNI 调用之间缓存了 JNIEnv*。并来自comment我开始知道它是无效的,这导致我学习 JNI 本地和全局引用。我做了一些测试程序来理解它。从测试程序中我无法理解全局引用的使用。因为 Local 引用本身在多个 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 对象引用了它,或者因为垃圾收集器没有在调用之间的短时间内运行。您不应该依赖于此,而应保留对以后可能需要的任何内容的全局引用。

ArrayList 可以调整大小。字符串是不可变的,并且总是需要为 substring 或append 等函数创建一个新字符串。

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

关于Java JNI编程: Actual usage of the Global References,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32200931/

相关文章:

java - 是否可以在没有 JNI 的情况下使用 sun.misc.Unsafe 调用 C 函数?

java - 如何加快我的表转换算法?

java - Android JSON解析问题: seems to separate it into 2 separate objects and only recognizes one

android - 如何在 Android NDK 项目中包含二进制文件?

java - 禁用从 char* 到 java.lang.String 的转换

java - DLL 中函数的名称是什么?

java - 由: com. mongodb.MongoSocketReadException引起:接收消息异常

java - java中使用split方法时如何保留重复值

java - h :commandButton action is just called one time on two calls

java - JNI C 反射谜题