【问题标题】:"JNI ERROR (app bug): weak global reference table overflow" why?“JNI ERROR(应用程序错误):弱全局引用表溢出”为什么?
【发布时间】:2018-08-27 00:17:29
【问题描述】:

根据此线程,由于我的本机代码泄漏,我不断收到此错误:

ReferenceTable overflow (max=512) JNI

然而,在我看来 AttachCurrentThread 泄漏了。我试过这段代码,它泄漏了

// this code LEAKS!
// C++:
void Engine::UpdateCamera(float x, float y, float z) {
    JNIEnv *jni;
    app_->activity->vm->AttachCurrentThread(&jni, NULL);
    //Do nothing
    app_->activity->vm->DetachCurrentThread();
    return;
}


// Java
public void updateCamera(final float x, final float y, final float z) {
    if (_label2 == null)
        return;

    StackTraceElement trace = new Exception().getStackTrace()[0];
    Log.e(APP_TAG, "Called:" +
            trace.getClassName() + "->" + trace.getMethodName() + ":" + trace.getLineNumber());

}

然后我简单地注释掉所有内容,程序停止泄漏并永远运行:(

// this code never leaks, but it does not do anything either
void Engine::UpdateCamera(float x, float y, float z) {
    JNIEnv *jni;
    //app_->activity->vm->AttachCurrentThread(&jni, NULL);
    //app_->activity->vm->DetachCurrentThread();
    return;
}

有没有人遇到过 AttachCurrentThread 的泄漏问题?

谢谢。

【问题讨论】:

  • 我没有看到你删除了对“消息”的引用
  • 要清楚,即使删除了所有创建本地引用的代码,您的错误仍然是引用表溢出?你的原生线程总是一样的吗?如果是这样,一种解决方法可能是在该线程即将死亡时仅分离一次(即使这并不能解开谜团)。
  • 是的,我不断收到这个烦人的引用表溢出,只调用 AttachCurrentThread/DettachCurrentThread。我只有一个渲染线程。所以我猜我不能从渲染线程到 Java 进行多次调用(每帧 60 个)。我可以解决这个问题,但这也不是正确的解决方法。
  • @gmmo 所以它运行 46,000 次来填充最多 512 个条目的表?你能在另一个平台上复制这个问题,比如 Linux 吗?我怀疑 Android JVM 中存在某种错误/资源泄漏。填满时参考表中的内容是什么?
  • @Andrew 我怀疑虚拟机在分离后需要一些空闲时间来清理,也许它没有得到那个,只有 46,000 的一些引用被卡住了。类似于需要完成太多对象时发生的情况。这就是为什么我想知道不分离(即不总是创建新的线程对象只是为了再次丢弃它们)是否有帮助。

标签: android-ndk java-native-interface


【解决方案1】:

你连接到调试器了吗?如果是这样,断开连接,您可能会发现弱引用表恢复到合理的值。

我遇到了同样的问题;如果我使用调试器运行,就会发生这种情况。

【讨论】:

【解决方案2】:

下面的attatchTestMemoryLeak()函数发生了native memory泄漏,我还没弄清楚原因,但我确实找到了另一种避免native memory泄漏的方法;见函数attatchTestOK();

//c++ code
void attatchTestMemoryLeak(){
    for(int i=0; i<100000; i++){
        JNIEnv *env= nullptr;
        //native thread try to attach java environment;
        int getEnvStat = g_VM->GetEnv((void **)&env,JNI_VERSION_1_4);
        if (getEnvStat == JNI_EDETACHED) {
            jint attachStat=g_VM->AttachCurrentThread(&env, NULL);
            if (attachStat == JNI_OK) {
                LOG_E("index=%d, attach ok",i);
            }else{
                LOG_E("index=%d, attach failed",i);
            }
        }

        //do something, call java function;

        //Detatched the native thread from java environment;
        jint detachStat=g_VM->DetachCurrentThread();
        if(detachStat==JNI_OK){
            LOG_E("detach ok, index=%d, detachStat=%d",i,detachStat);
        }else{
            LOG_E("detach failed, index=%d,detachStat=%d",i,detachStat);
        }
        env = NULL;
    }
}

下面的函数运行良好,https://www.jianshu.com/p/1f17ab192940 给出了解释。

static pthread_key_t detachKey=0;
void detachKeyDestructor(void* arg)
{
    pthread_t thd = pthread_self();
    JavaVM* jvm = (JavaVM*)arg;
    LOG_E("detach thread, thd=%u",thd);
    jvm->DetachCurrentThread();
}
void attachTestOK(){
    for (int i = 0; i < 1000000; i++)
    {
        JNIEnv *env= nullptr;
        int getEnvStat = g_VM->GetEnv((void **)&env,JNI_VERSION_1_4);
        if (getEnvStat == JNI_EDETACHED) {
            if (detachKey == 0){
                LOG_E("index=%d,create thread key",i);
                pthread_key_create(&detachKey, detachKeyDestructor);
            }

            jint attachStat=g_VM->AttachCurrentThread(&env, NULL);
            pthread_setspecific(detachKey, g_VM_Test);
            if (attachStat == JNI_OK) {
                LOG_E("index=%d, attach ok",i);
            }else{
                LOG_E("index=%d, attach failed",i);
            }
        }
        LOG_E("index=%d, getEnvStat=%d",i,getEnvStat);

        //do something, call java function;

        env = NULL;
    }
}

【讨论】:

    猜你喜欢
    • 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
    相关资源
    最近更新 更多