当前位置: 首页 > 知识库问答 >
问题:

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

公羊光明
2023-03-14

在我之前的问题中,我在JNI调用之间缓存了JNIEnv*。从评论中我知道它是无效的,这导致我学习了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

提前感谢。

共有1个答案

唐元凯
2023-03-14

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

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

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

 类似资料:
  • 本文向大家介绍ruby元编程实际使用实例,包括了ruby元编程实际使用实例的使用技巧和注意事项,需要的朋友参考一下 很喜欢ruby元编程,puppet和chef用到了很多ruby的语言特性,来定义一个新的部署语言。 分享几个在实际项目中用到的场景,能力有限,如果有更优方案,请留言给我:) rpc接口模板化——使用eval、alias、defind_method 上面是一个rpc server,编写

  • 问题内容: 您如何衡量Linux中应用程序或进程的内存使用情况? 从了解Linux上的内存使用的博客文章中可以看出,这不是用于此目的的准确工具。 为什么是“错” 根据您的看法,它不会报告进程的实际内存使用情况。它的真正作用是显示 如果 每个进程 是唯一运行的进程 , 则 每个进程将占用多少实际内存。当然,一台典型的Linux机器在任何给定时间都运行着几十个进程,这意味着报告的VSZ和RSS编号几乎

  • 公司一个比较大的老项目,vue2,需要做国际化,目前的方案是劫持dom然后做一个映射表进行替换(类似于浏览器自带的翻译)。但是,由于存在大量echart图表,绘制出来的是canvas,就无法获取到dom中的文字。想问一下如何在尽量少入侵代码的情况下,完成echart的国际化?(项目中没有使用i18n,图表涉及的文件近百个,也不好再重新修改,入侵性过大) 想着能不能再echart初始化的时候判断环境

  • 问题内容: 根据番石榴的文档: 警告:在大多数情况下,最好设置每个缓存的最大大小,而不要使用软引用。仅当您熟悉软引用的实际结果时,才应使用此方法。 我对软引用有一个中间的了解- 它们的行为,用途以及它们与垃圾回收的约定。但是,我想知道文档所暗示的这些实际后果是什么。为什么最好使用最大大小而不是软引用?就实现缓存而言,软引用的算法和行为是否不使它们的使用效率比硬编码上限高? 问题答案: 我认为他们所

  • 我有一个简单的类在Java: 在C中,我执行以下JNI调用: 如果一个程序调用静态方法c_call_function(),则可以。 但是如果一个多线程程序调用c_call_function(),当传递env行时,它会给我以下消息- 访问违规在0x0000006FC77154读取到0x0000000000000000 如果程序是多线程的,则使用相同的JNIEnv变量。但我也尝试通过AttachCur

  • 问题内容: 我正在计划在Windows下运行的Java程序的计划制定工作,该程序可以在背景的不同键runnig上映射不同的“宏”。问题是- 如何使Java在应用程序未聚焦时监听按键。 我发现很多观点认为这是不可能的。但是我也发现Stefano在SO上写的这本书。这种解决方案对我来说还不够好,至少没有一个重要的信息。如果没有按下该键,该函数将返回一个值。按键后,它会返回不同的值…如果函数在按键事件后