我的应用需要使用jni。逻辑看起来像:
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
}
通过JNI doc,GetStringUTFChars返回的数组应该使用
进行关联(*env)->ReleaseStringUTFChars(env, jstr, myObjArray[i].name);
(*env)->ReleaseLocalRef(env, jstr);
我的问题是: 如果我想正确释放jstring,我该怎么办?
答案 0 :(得分:6)
由于你的循环正在创建本地引用(GetObjectField),你需要在循环中释放它(DeleteLocalRef),否则你将遇到本地引用的限制。您必须在两次调用之间完全处理Java字符串。
由于您希望保留字符串的字节以便在循环外使用,因此需要复制字节,因为在字符串引用之前必须释放JVM的固定(或临时副本)(GetStringUTFChars)(ReleaseStringUTFChars)释放。
因此循环内字符串的顺序必须是:
GetObjectField
GetStringUTFChars
ReleaseStringUTFChars
DeleteLocalRef
注意:使用GetStringUTFChars
,您将获得指向Java String的修改后的UTF-8编码的指针。这里有两点:
GetStringUTFLength
来获取修改后的UTF-8编码中的字节数 - 不计算任何0终止符。 (各种JNI实现和The Book都同意该数组以0结尾。)如果您想使用终结符创建自己的副本,请确保为终结符添加空间。如果您更喜欢使用UTF-16编码,请使用GetStringChars
和GetStringLength
。在这种情况下,阵列绝对不会终止;它使用内部计数和字符串字节而不进行任何转换。
或者,如果您想更改字符集,例如真正的“UTF-8”,“ASCII”,“CP437”或“Windows-1252”或您的代码可以处理的其他内容,请使用{{1} } overload或String.getBytes
类。如果要控制如何处理目标字符集中不支持的字符,请使用Charset
类。
答案 1 :(得分:0)
经过一些实验,我得到了答案,但我不确定它是否完全正确。 我在循环中删除了jstring的引用,错误“添加到JNI本地引用表失败(有512个条目)”不再发生了。
void myJniFunc(JNIEnv *env, jclass clazz, jobjectArray items) {
int count = 10;
jstring tempArray[count];
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
tempArray[i] = jstr;
(*env)->DeleteLocalRef(jstr);
}
// some code which will use myObjArray
process(count, myObjectArray);
// Location B
for (i = 0; i < count; i++) {
(*env)->ReleaseStringUTFChars(env, tempArray[i], myObjectArray[i].name);
}
}
我担心的是: 函数“ReleaseStringUTFChars”的第二个参数需要是一个jstring。 所以我创建了一个数组来保存jstring的引用,以便以后发布。 因为我在循环中删除了jstring的引用,这意味着释放了jstring。 我在这个jstring上调用“ReleaseStringUTFChars”有什么问题吗? 通过我的测试,我没有遇到任何问题。
答案 2 :(得分:0)
除了Tom Blodget的帖子外,我还可以添加:请勿在jstring对象上使用DeleteLocalRef方法,并且请勿在第一个中将Java字符串从jobject转换为jstring。放置(例如,当您从java方法中检索字符串时)!将其保留在jobject类型引用中,仅在将其作为参数传递时才将其转换为jstring,否则会出现“ JNI错误(应用程序错误):访问过时的本地”错误! 例如,请勿执行以下操作:
jstring imeiObj = (jstring)env->CallObjectMethod(telephonyManagerObj, jniInfo->jniCache.telephonyManagerClass_getDeviceIdMethod, i);
const char* imeiStr = env->GetStringUTFChars(imeiObj, &isCopy);
env->DeleteLocalRef((jobject)imeiObj);
最好做:
jobject imeiObj = env->CallObjectMethod(telephonyManagerObj, jniInfo->jniCache.telephonyManagerClass_getDeviceIdMethod, i);
const char* imeiStr = env->GetStringUTFChars((jstring)imeiObj, &isCopy);
env->DeleteLocalRef(imeiObj);
我不知道为什么会这样