When I run the code, I get an error \"failed adding to JNI local ref table has 512 entries\"
This is my code:
jstring pJNIData = pJNIEnv->NewStrin
I thought I would chip in just in case anyone else runs into this issue. This is a weird case that kept me confused for hours!
Ok so I have an NDK app and the Java code being called is inside an apk that is loaded at runtime. I have no idea if the runtime loading effects this in any way but I thought I should mention it.
Now in a c++ method I use find class and getmethodid to get the constuctor to a HashMap and call it to get a new HashMap instance. I then populate the HashMap from the c++ side using jni calls. So far so good. I then pass the HashMap to java code and, again, all is working as expected. Once the java code has returned I call DeleteLocalRef on the HashMap. No errors are thrown but the reference is not deleted.
This only came up when I finally ran over 512 local references (from multiple calls to this function) and the error dump showed that the last 10 items in the localref store were nearly all HashMaps. I would understand that the GC would not collect these references at the end of the method as I am make a multithreaded ndk app. However the DeleteLocalRef should have worked.
The Fix: In the end I found that creating the HashMap from a jni call to a java method I wrote was fine, and the reference was then free'able. It seems crazy to have a java function that literally just returns a new HashMap but it worked so for now I am living with it :)
I have seen this when a JNI method called Java code (in my case, the method was not static). As I understand, unused local references are not automatically deleted when a Java method is called from JNI (I mean, until the top-level JNI function returns).
IIRC either there already was information about memory objects in the log, or I could add some logging; from that information I identified garbage items that I did not mention before. They were two arrays and a class, created in subsequent calls but not garbage-collected.
// in a function that calls a Java method from JNI
jbyteArray srcArray = env->NewByteArray(len);
jclass cls = env->FindClass("com/something/MyClass");
jmethodID mid = env->GetMethodID(cls, "mymethod", "([BI)[B");
jbyteArray resArray = (jbyteArray)env->CallObjectMethod(obj, mid, srcArray, XXXX);
...
env->DeleteLocalRef(cls);
env->DeleteLocalRef(resArray);
env->DeleteLocalRef(srcArray);
// no need to do anything with mid
Note that although these three local references were obtained differently, all of them were hanging around.
Useful link: http://www.netmite.com/android/mydroid/dalvik/docs/jni-tips.html#local_vs_global_references (or find the Dalvik VM docs dalvik/docs/jni-tips.html and locate the section "Local vs. Global References")
Every object that JNI returns is a "local reference". This means that it's valid for the duration of the current native method in the current thread. Even if the object itself continues to live on after the native method returns, the reference is not valid. This applies to all sub-classes of jobject, including jclass and jarray. [...] Note: method and field IDs are just 32-bit identifiers, not object references, and should not be passed to NewGlobalRef. The raw data pointers returned by functions like GetStringUTFChars and GetByteArrayElements are also not objects.