简体   繁体   中英

JNI Error: accessed stale weak global reference

I cache a reference to a Java object in my native code, just like this:

// java global reference deleter
// _JAVA_ENV  is an instance of JNIEnv that is cached globally and just
// valid in current thread scope
static void g_java_ref_deleter(jobject ptr) {
   _JAVA_ENV->DeleteGlobalRef(ptr);
}

// native class caches a java object reference
class NativeA {
private:
    shared_ptr<_jobject> M_java_object;
public:
    setJavaObject(jobject obj) {
        M_java_object = shared_ptr<_jobject>(_JAVA_ENV->NewGlobalRef(obj), g_java_ref_deleter);
    }

    shared_ptr<_jobject> getJavaObject() const {
        return M_java_object;
    }
}

and I access it in another native class:

class NativeB {
public:
    void doSomething(NativeA& a) {
        // here I got an error: accessed stale weak global reference
        // make_record do something on java object (set float field actually)
        make_record(a.getJavaObject().get());
    }
}

This code run onto Android 4.3. Why do I get this error and how can I fix it?

OK, I've solved this problem! Actually I cached _JAVA_ENV and use it mistakenly. From this blog I found :

Although any given JNIEnv* is only valid for use on one thread, because Android never had any per-thread state in a JNIEnv*, it used to be possible to get away with using a JNIEnv* on the wrong thread. Now there's a per-thread local reference table, it's vital that you only use a JNIEnv* on the right thread.

So I thought there is no problem that I cache the JNIEnv and use it in one thread, but actually the JNIEnv was stale when the program get into java environment and returned to native environment. (eh... forgive my poor English)

And from the documentation , I found :

If a piece of code has no other way to get its JNIEnv, you should share the JavaVM, and use GetEnv to discover the thread's JNIEnv.

So, you should cache the JavaVM, and use it to get JNIEnv , the code would be like this :

JavaVM* g_jvm;

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    g_jvm = vm;
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }

    // Get jclass with env->FindClass.
    // Register methods with env->RegisterNatives.

    return JNI_VERSION_1_6;
}

JNIEnv* getJNIEnv() {
    JNIEnv* env;
    g_jvm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6 /*version*/);
    return env;
}

Hope can help someone! (please forgive my poor English again..)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM