考虑以下C代码段。
第1部分:
char * getSomeString(JNIEnv *env, jstring jstr) {
char * retString;
retString = (*env)->GetStringUTFChars(env, jstr, NULL);
return retString;
}
void useSomeString(JNIEnv *env, jobject jobj, char *mName) {
jclass cl = (*env)->GetObjectClass(env, jobj);
jmethodId mId = (*env)->GetMethodID(env, cl, mName, "()Ljava/lang/String;");
jstring jstr = (*env)->CallObjectMethod(env, obj, id, NULL);
char * myString = getSomeString(env, jstr);
/* ... use myString without modifing it */
free(myString);
}
因为myString在useSomeString中被释放,所以我认为我没有创建内存泄漏;但是,我不确定。 JNI规范特别要求使用ReleaseStringUTFChars。由于我从GetStringUTFChars获取了一个C风格的'char *'指针,我相信内存引用存在于C堆栈而不是JAVA堆中,因此它没有被Garbage Collected的危险;但是,我不确定。
我知道如下更改getSomeString会更安全(也许更可取)。
第2部分:
char * getSomeString(JNIEnv *env, jstring jstr) {
char * retString;
char * intermedString;
intermedString = (*env)->GetStringUTFChars(env, jstr, NULL);
retString = strdup(intermedString);
(*env)->ReleaseStringUTFChars(env, jstr, intermedString);
return retString;
}
由于我们的'过程',我需要建立一个论证,说明为什么段2中的getSomeString优于段1。
是否有人知道任何详细说明GetStringUTFChars和ReleaseStringUTFChars与分配内存或执行了哪些(如果有)额外簿记(即创建Java堆的本地参考指针等)的行为的文档或参考文献。无视簿记的具体后果是什么。
提前致谢。
答案 0 :(得分:4)
您无需关心实施细节;如果GetStringUTFChars的文档说你必须使用ReleaseStringUTFChars,那么就这样做吧。
大多数库提供自己的功能来释放他们分配的内存,因为他们的CRT / heap / ...可能与你的应用程序不同,所以如果你试图免费使用你的 他们的指针,你可能会在你身边获得相当于双倍免费的指针,而在库方面则会出现内存泄漏。
我会再说一遍:不依赖于实施细节。图书馆作者相信每个人都遵循他们的指导方针,所以即使今天的免费可能有效,明天他们也可能决定使用另一个堆/分配器/ ......如果你遵循他们的指导方针,你会体验到没问题,因为他们会相应地更新ReleaseStringUTFChars,但如果您依赖于该实现细节,您的应用程序可能会突然开始死亡或遇到内存泄漏。
答案 1 :(得分:1)
我们最终发现了什么。
链接Java和C的应用程序将共享堆空间。这意味着在事件的C端分配的任何内存都不能在为Java保留的堆部分中分配malloc(et.al.)。但是,由JNI函数本身分配的任何内存可能会也可能不会在堆的Java端分配空间(它是JNI的实现细节)。这留下了两种可能性:1)从堆的C端分配内存,其中调用free不会产生任何不良影响; 2)从Java端分配内存将打开GC在C侧完成之前回收内存的可能性。面对这种不确定性,最安全的方法是使用malloc显式分配新空间,释放JNI内存,并在不再需要新分配的内存时自由调用。
我要感谢Matteo Italia对此的帮助。