【问题标题】:JNI "local reference table overflow" when using NewGlobalRef使用 NewGlobalRef 时 JNI “本地引用表溢出”
【发布时间】:2025-12-31 03:25:12
【问题描述】:

我正在开发一个使用 JNI 的应用,但在使用全局引用时出现“本地引用表溢出”错误:

art/runtime/indirect_reference_table.cc:115] JNI ERROR (app bug): local reference table overflow (max=512)
 art/runtime/indirect_reference_table.cc:115] local reference table dump:
 art/runtime/indirect_reference_table.cc:115]   Last 10 entries (of 508):
 art/runtime/indirect_reference_table.cc:115]       507: 0x13f08b80 com.company.util.jni.JniCompanyHelper$HTTPStream
 art/runtime/indirect_reference_table.cc:115]       506: 0x13ef5520 com.company.util.jni.JniCompanyHelper$HTTPStream
 art/runtime/indirect_reference_table.cc:115]       505: 0x13eb4a80 com.company.util.jni.JniCompanyHelper$HTTPStream

代码如下:

class Foo {
public:
    Foo(){}
    void doSomething(){
        JNIEnv* env = getEnv();
        javaObject = env->NewGlobalRef(env->CallStaticObjectMethod(...));
    }
    ~Foo() {
        JNIEnv* env = getEnv();
        env->DeleteGlobalRef(javaObject);
    }
private:
    jobject javaObject;
};

// ...
// Run the loop in a separate thread
for(int i =0; i< 1000; i++) {
    Foo foo;
    foo.doSomething();
}

当我创建一个创建和销毁 1000 个 Foo 实例并运行 doSomething 的大循环时,我收到错误“本地引用表溢出”但如果我不使用 NewGlobalRef,如下所示,我不会遇到任何崩溃:

class Foo {
public:
    Foo(){}
    void doSomething(){
        JNIEnv* env = getEnv();
        jobject javaObject = env->CallStaticObjectMethod(...);
        env->DeleteLocalRef(javaObject);
    }
    ~Foo() {
    }
};

有人知道我在这里缺少什么吗?

【问题讨论】:

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


    【解决方案1】:

    CallStaticObjectMethod 返回一个对您正在创建的任何内容的本地引用(这意味着该本地引用将被添加到当前线程的本地引用表中)。

    除非您返回 java 或从 VM 中分离本机线程,否则该引用表不会被清除。而且,如果您在不清除本地引用表的情况下继续创建本地引用,您最终会用完表中的空闲条目。

    您的固定版本通过在您完成使用后立即删除每个本地引用来解决此问题。

    全局引用的要点是当你返回java或分离当前线程时它们不会被自动删除,所以你以后可以使用相同的引用。但是在您的情况下,您似乎不需要它,尽管您的示例看起来有点做作(您创建了从未使用过的对象)。

    【讨论】:

    • 例子被简化了。当Foo 析构函数被调用时,它会调用DeleteGlobalRef,但是从env-&gt;CallStaticObjectMethod 返回的本地引用在创建全局引用后不会被删除。所以修复只是在创建全局引用后删除本地引用。谢谢!
    • 您可能不想在析构函数中无条件调用DeleteGlobalRef,因为您可以在不创建全局引用的情况下构造Foo