Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to release jstring in a loop correctly?

My app needs to use jni. Logic looks like :

void myJniFunc(JNIEnv *env, jclass clazz, jobjectArray items) {
    int count = 10;
    struct MyObj *myObjArray = (struct MyObj*)malloc(sizeof(struct MyObj) * count);
    for (i = 0; i < count; i++) {
        jobject obj = (*env)->GetObjectArrayElement(env, items, i);
        jfieldID fieldId = ...;
        jstring jstr = (*env)->GetObjectField(env, obj, fieldId);
        myObjArray[i].name = (*env)->GetStringUTFChars(env, jstr);
        (*env)->DeleteLocalRef(env, obj);
        // Location A
    }

    // some code which will use myObjArray
    process(count, myObjectArray);

    // Location B
}

And through JNI doc, the array return by GetStringUTFChars should be relased using

(*env)->ReleaseStringUTFChars(env, jstr, myObjArray[i].name);
(*env)->ReleaseLocalRef(env, jstr);
  1. If I release the returned array at Location A, then myObjArray.name will be empty
  2. If I release the returned array at Location B, as I will keep the reference of jstring, then "Failed adding to JNI local ref table(has 512 entries)" will happen

My question is : What should I do, if I want to release jstring correctly?

like image 985
cmoaciopm Avatar asked Jul 08 '13 04:07

cmoaciopm


1 Answers

Since your loop is creating the local ref (GetObjectField), you need to release it (DeleteLocalRef) in the loop, or you'll run up against the limit on local references. You'll have to completely process the Java string between to two calls.

Since you want to keep the bytes of the string for use outside of the loop, you need to copy the bytes because the JVM's pinning (or temporary copy)(GetStringUTFChars) has to be released (ReleaseStringUTFChars) before the string reference is released.

So the sequence for the string inside the loop must be:

  1. GetObjectField
  2. GetStringUTFChars
  3. make your own copy
  4. ReleaseStringUTFChars
  5. DeleteLocalRef

Note: With GetStringUTFChars you are getting a pointer to a modified UTF-8 encoding of the Java String. Two points here:

  1. Your code should be able to handle modified UTF-8 encoded characters. (It has between one and six bytes per character and encodes NUL in a peculiar way.)
  2. The documentation doesn't say if the array is 0-terminated. You can use GetStringUTFLength to get the number of bytes in the modified UTF-8 encoding—not counting any 0-terminator. (Various JNI implementations and The Book do agree that the array is 0-terminated.) If you want to make your own copy with a terminator, be sure to add room for the terminator.

If you'd rather use a UTF-16 encoding, use GetStringChars and GetStringLength. In this case, the array is definitely not terminated; It uses the internal count and string bytes without any conversion.

Or, if you want to change character sets, to say, real "UTF-8", "ASCII", "CP437" or "Windows-1252" or something else your code can handle, use a String.getBytes overload or the Charset class. Use the Charset class if you want control over how to handle characters that are not supported in the target character set.

like image 80
Tom Blodget Avatar answered Sep 18 '22 03:09

Tom Blodget