如何在一个循环正确释放的jstring?正确、如何在、jstring

2023-09-08 10:16:35 作者:装酷不是我的style

我的应用程序需要使用JNI。逻辑是这样的:

 无效myJniFunc(JNIEnv的* ENV,JCLASS clazz所jobjectArray项目){    诠释计数= 10;    结构MyObj中* myObjArray =(结构MyObj中*)malloc的(的sizeof(结构MyObj中)*计);    对于(i = 0; I<计数;我++){        jobject OBJ =(* ENV) -  GT; GetObjectArrayElement(环境,物品,我);        jfieldID fieldId = ...;        的jstring jstr =(* ENV) -  GT; GetObjectField(ENV,OBJ,fieldId);        myObjArray [我]。名称=(* ENV) -  GT; GetStringUTFChars(ENV,jstr);        (* ENV) -  GT; DeleteLocalRef(ENV,OBJ);        //一个位置    }    //一些code将使用myObjArray    流程(计数,myObjectArray);    // B位置} 

和通过JNI文档,通过GetStringUTFChars阵列回报应该使用relased

 (* ENV) -  GT; ReleaseStringUTFChars(ENV,jstr,myObjArray [我]。名称);(* ENV) -  GT; ReleaseLocalRef(ENV,jstr); 

如果我释放返回数组在位置A,然后myObjArray.name将是空如果我释放返回数组在位置B,我会继续的jstring参考,然后无法加入当地的JNI参考表(有512项)将会发生

我的问题是:我应该怎么做,如果我想正确释放的jstring?

解决方案

由于您的循环创建本地REF(GetObjectField),您需要在循环释放它(DeleteLocalRef),否则你会反对极限跑起来本地引用。你必须完全处理两个电话之间的Java字符串。

由于要保留字符串中使用的字节的循环之外,你需要复制的字节数,因为JVM的钉扎(或临时副本)(GetStringUTFChars)具有字符串引用是之前被释放(ReleaseStringUTFChars)释放。

因此​​,对于循环内的字符串的顺序必须是:

GetObjectField GetStringUTFChars 请自己的副本 ReleaseStringUTFChars DeleteLocalRef

注意:使用 GetStringUTFChars 你得到一个指向Java的String的修改UTF-8编码。这里有两点:

您$​​ C $ C应该能够处理UTF-8修改连接codeD字符。 (它具有每字符和连接codeS NUL以一种特殊的方式字节一到六之间。)如果数组是0终结的文档没有说。您可以使用 GetStringUTFLength 来得到的字节数的经修订的UTF-8编码,不计任何0终止符。 (JNI各种实现和的书的不同意数组是0终结。 )如果你想用一个终端,使自己的副本,一定要为终结添加房间。双循环 如何循环起来 从 出不去 与 进不来 谈起

如果您宁愿使用UTF-16编码,使用 GetStringChars GetStringLength 。在这种情况下,该阵列是绝对不会终止;它使用内部计数和字符串字节无需任何转换。

或者,如果你想改变字符集,说,真正的UTF-8,ASCII,CP437或视窗1252或别的东西你code可以处理,使用 String.getBytes 过载或字符集类。如果你想在如何处理未在目标字符集支持的字符控制使用字符集类。

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

If I release the returned array at Location A, then myObjArray.name will be empty 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?

解决方案

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:

GetObjectField GetStringUTFChars make your own copy ReleaseStringUTFChars DeleteLocalRef

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

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.) 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.