【问题标题】:What causes the JNI error "use of deleted local reference"?是什么导致 JNI 错误“使用已删除的本地引用”?
【发布时间】:2018-07-16 17:38:36
【问题描述】:

我有一个 Android 应用程序,在该应用程序启动时调用以下 C 方法(在Activity.onCreate 中)。

extern "C"
JNIEXPORT jstring JNICALL
Java_com_google_oboe_test_oboetest_MainActivity_stringFromJNI(
    JNIEnv *env,
    jobject instance) {

    jclass sysclazz = env->FindClass("java/lang/System");
    jmethodID getPropertyMethod = env->GetStaticMethodID(sysclazz, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
    jstring result = (jstring) env->CallStaticObjectMethod(sysclazz, getPropertyMethod, "os.name");
    return result;
}

当调用此方法时,应用程序崩溃并出现错误:

应用程序中检测到 JNI 错误:使用已删除的本地引用 0xd280e8d5

单步调试显示这行导致崩溃:

jstring result = (jstring) env->CallStaticObjectMethod(sysclazz, getPropertyMethod, "os.name");

是什么导致了这个错误?以及如何使用 JNI 调用 System.getProperty("os.name") 而不会出现此错误?

【问题讨论】:

    标签: java android c jvm java-native-interface


    【解决方案1】:

    问题在于 env->CallStaticObjectMethod 期望 jstring 作为其第三个参数,而是提供了一个字符串文字。

    首先创建一个jstring

    jstring arg = env->NewStringUTF("os.name");
    jstring result = (jstring) env->CallStaticObjectMethod(sysclazz, getPropertyMethod, arg);
    

    解决了问题。

    【讨论】:

    • 请记住使用DeleteLocalRef 释放那些jstring 变量,例如env->DeleteLocalRef(arg);env->DeleteLocalRef(result);,否则您的代码中会出现内存泄漏。
    • @shizhen:没必要因为stringFromJNI会返回java,会自动清除已经创建的本地引用。
    • 理论上你让 JVM 为你释放那些本地 refs 是真的。但是,删除由您的代码创建的本地引用更像是一种“最佳实践”。因为,本地引用会消耗大量的 JVM 资源,尤其是当您在长循环中创建/使用它时,过度分配本地引用可能会导致 VM 在执行本地方法期间耗尽内存。对于这种情况,它是如此简单,以至于它可能并不重要。当然,要不要这样做,这完全取决于你自己。
    • @EJP,是的,正如我所说,对于这种情况来说,这并不重要,这种情况太简单了,无法说明尽早删除本地引用的重要性。我想在这里说明的一点是牢记这种编码实践。
    【解决方案2】:

    就我而言,我使用的是在一个函数中创建并在另一个函数中使用的本地引用。 例如:

    void funA(){
        jclass localClass = env->FindClass("MyClass");
    }
    

    上面的语句返回一个 jclass 的本地引用。假设我们将它存储在一个全局变量中,并在 funA 完成后在另一个函数 funB 中使用它,那么这个本地引用将不会被认为是有效的,并且会返回“使用已删除的本地引用错误”。

    要解决这个问题,我们需要这样做:

    jclass localClass = env->FindClass("MyClass");
    jclass globalClass = reinterpret_cast<jclass>(env->NewGlobalRef(localClass));
    

    首先,获取本地引用,然后从本地引用获取全局引用。 globalClass 可以全局使用(在不同的功能中)。

    阅读Local and global references

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-20
      相关资源
      最近更新 更多