4

I keep getting this error due to a leak in my native code according to this thread:

ReferenceTable overflow (max=512) JNI

yet, it seems to me the the AttachCurrentThread leaks. I tried this code and it leaks

// 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());

}

Then I simply commented out everything and the program stopped leaking and ran forever :(

// 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;
}

Has anyone experienced leaking issues with AttachCurrentThread?

thank you.

Community
  • 1
  • 1
gmmo
  • 2,577
  • 3
  • 30
  • 56
  • 1
    I don't see you deleting the references to "message" – user2543253 Oct 17 '16 at 09:55
  • I removed everything and still leaks. Seems like the AttachCurrentThread is causing problems. If you can lead me to the answer, 50 points from my reputation is for grabs. – gmmo Oct 18 '16 at 19:18
  • How many threads do you have running? How often do you call `Engine::UpdateCamera()` before you get the error? – Andrew Henle Oct 18 '16 at 22:13
  • It is based off the sample "https://github.com/googlesamples/android-ndk/tree/master/teapots/more-teapots" I believe it is only one rendering thread that uses "glue". Yet, I do call the "UpdateCamera" several times. One call per frame @ 60 hz to be more precise. – gmmo Oct 18 '16 at 22:37
  • On my S6 galaxy it takes about 13 minutes to crash with the overflow. So I estimated the UpdateCamera gets called somewhat 46k times before it blows up. 13 min * 60 (seconds/min) * 60 (times/second) – gmmo Oct 18 '16 at 23:11
  • 1
    To be clear, your error is still a reference table overflow even after removing all code that creates local references? Is your native thread always the same? If so a workaround could be to detach only once when that thread is about to die (even if that doesn't solve the mystery). – user2543253 Oct 19 '16 at 11:24
  • 1
    yes, I keep getting this annoying reference table overflow with only calls to AttachCurrentThread/DettachCurrentThread. I have only one rendering thread. so I am guessing i cannot make several calls (60 per frame) from the rendering thread to Java. I can work around this, but it is not a proper fix either. – gmmo Oct 19 '16 at 17:50
  • 1
    @gmmo So it runs 46,000 times to fill up a table with a max of 512 entries? Can you duplicate the problem on another platform, such as Linux? I suspect a bug/resource leak of some sort in the Android JVM. What's in the reference table when it fills up? – Andrew Henle Oct 20 '16 at 13:37
  • 2
    @Andrew My suspicion would be that the VM needs some spare time to clean up after the detach and maybe it doesn't get that and only some references of the 46,000 get stuck. Similar to what happens when too many objects need to be finalized. That's why I'd like to know if not detaching (i.e. not always creating new thread objects only to throw them away again) helps. – user2543253 Oct 20 '16 at 14:25
  • @user2543253 thanks for the help. I ended up simply going around the problem and not calling update many times. The original post was not clear. I get the similar error of max = 512, but with my samsung s6 phone the max = 52k entries. Which matches closes the amount of times I call update. I guess this is probably a limitation. – gmmo Nov 01 '16 at 19:19

2 Answers2

4

Are you connected to the debugger? If so, disconnect and you may find the weak reference table returns to a reasonable value.

I had this same problem; if I run with the debugger, this occurs.

Biscuit
  • 41
  • 2
  • This does not provide an answer to the question. Once you have sufficient [reputation](https://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](https://stackoverflow.com/help/privileges/comment); instead, [provide answers that don't require clarification from the asker](https://meta.stackexchange.com/questions/214173/why-do-i-need-50-reputation-to-comment-what-can-i-do-instead). - [From Review](/review/low-quality-posts/17077683) – miken32 Aug 18 '17 at 23:10
  • 4
    @miken32 No this is a valid answer, it tells people to disconnect and can solve the problem. – Papershine Aug 19 '17 at 08:46
2

The below attatchTestMemoryLeak() function has a native memory leak, and I still have not figured out the reason, but I did find another way to avoid the native memory leak; see function 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;
    }
}

The below function works fine, and the https://www.jianshu.com/p/1f17ab192940 gives the explanation.

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;
    }
}
Roy Scheffers
  • 3,832
  • 11
  • 31
  • 36
wujin
  • 21
  • 3