【问题标题】:Java JNI Programming : Actual usage of the Global ReferencesJava JNI 编程:全局引用的实际用法
【发布时间】:2015-08-25 18:29:57
【问题描述】:

在我的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

提前致谢。

【问题讨论】:

  • 如果 JNI 规范不支持某些工作,那么您很幸运。你不能依赖运气。
  • @EJP,谢谢。现在我可以得出一个了解本地和全球参考的结论

标签: java java-native-interface


【解决方案1】:

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

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

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

【讨论】:

  • 非常感谢。我得到了我的问题的答案。并且能够比以前更好地理解本地和全局引用。
  • 如果我在使用全局引用 ("obj2 = (jobject) env->NewGlobalRef(str);") 的情况下第二次调用第二个 JNI 函数是否无效。因为在我的第二次通话中,我正在创建新的字符串对象。也就是说,在 java 代码中,第一个字符串对象不会被垃圾收集,直到我使用 DeleteGlobalRef()?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多