JNI错误(应用程序错误):弱全局引用表溢出。为什么会出现这种情况?

4

根据这个帖子,由于本机代码泄漏,我不断地收到此错误提示:

ReferenceTable溢出(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泄漏的问题吗?

谢谢。


1
我没有看到你删除对“message”的引用。 - user2543253
1
请明确一点,即使删除所有创建本地引用的代码后,您的错误仍然是引用表溢出吗?您的本机线程始终相同吗?如果是这样,解决方法可能是仅在该线程即将死亡时分离一次(即使这不能解决谜团)。 - user2543253
1
是的,我一直遇到这个烦人的引用表溢出问题,只有对AttachCurrentThread/DettachCurrentThread的调用。我只有一个渲染线程,所以我猜想我不能从渲染线程向Java发出多个调用(每帧60次)。我可以绕过这个问题,但这也不是一个适当的解决方法。 - gmmo
1
@gmmo 所以它运行了46,000次来填充一个最大512条目的表格?您能在另一个平台上复制该问题,例如Linux吗?我怀疑Android JVM中存在某种错误/资源泄漏。当它填满参考表时,里面有什么? - Andrew Henle
2
@Andrew 我的猜测是虚拟机需要一些空闲时间来清理分离后的内容,但可能没有得到这个时间,只有 46,000 个引用被卡住了。就像太多对象需要完成时发生的情况一样。这就是为什么我想知道不分离(即不总是创建新线程对象然后再将它们丢弃)是否有所帮助。 - user2543253
显示剩余5条评论
2个回答

4

你是否连接到调试器?如果是,请断开连接,可能会发现弱引用表返回到合理的值。

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


这并没有回答问题。一旦您拥有足够的声望,您将能够评论任何帖子;相反,提供不需要询问者澄清的答案。- 来自审核 - miken32
4
不,这是一个有效的答案,它告诉人们断开连接可以解决问题。 - Papershine

2
以下的attatchTestMemoryLeak()函数存在本地内存泄漏,我仍然没有找到原因,但我找到了另一种避免本地内存泄漏的方法,请参考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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接