views:

128

answers:

2

Consider the following C code segments.

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

Because myString is freed in useSomeString, I do not think I am creating a memory leak; however, I am not sure. The JNI spec specifically requires the use of ReleaseStringUTFChars. Since I am getting a C style 'char *' pointer from GetStringUTFChars, I believe the memory reference exists on the C stack and not in the JAVA heap so it is not in danger of being Garbage Collected; however, I am not sure.

I know that changing getSomeString as follows would be safer (and probably preferable).

Segment 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;
}

Because of our 'process' I need to build an argument on why getSomeString in Segment 2 is preferable to Segment 1.

Is anyone aware of any documentation or references which detail the behavior of GetStringUTFChars and ReleaseStringUTFChars in relation to where memory is allocated or what (if any) additional bookkeeping is done (i.e. local Reference Pointer to the Java Heap being created, etc). What are the specific consequences of ignoring that bookkeeping.

Thanks in advance.

+1  A: 

You don't need to care about implementation details; if the documentation of GetStringUTFChars says you that you must use ReleaseStringUTFChars, you do it, period.

Most libraries provide their own functions to free the memory they allocate because their CRT/heap/... may be different than the one of your application, so if you try to use your free on their pointers you may get on your side the equivalent of a double free, and on the library side a memory leak.

I'll say it again: do not rely on implementation details. The library authors are confident that everyone follows their guidelines, so even if a free today may work, tomorrow they may decide to use another heap/allocator/... If you followed their guidelines you'd experience no problem, because they would have updated ReleaseStringUTFChars accordingly, but if you relied on that implementation detail all of a sudden your application may start do die or to experience memory leaks.

Matteo Italia
Thank you for your response. I agree with you that reliance upon implementation details is a bad idea and a poor practice. However, I still need a reference which provides some sort of "official" declamation of the implementaion in Segment 1 above. With such I can start the arduous paper trail which eventually leads to a correction.
Bayou Bob
In my opinion it's easy that JNI uses its private heap, you could try to demonstrate it by comparing the addresses of pointers returned by JNI and memory allocated by malloc. If they are quite distant from each other you can easily support the claim that they come from different heaps, and that freeing a JNI pointer with your free is definitely not a good idea.
Matteo Italia
That was great idea. Unfortunately I would say it didn't pan out. --jstring parameter address: fe37ea44 -- jobject created with NewString: fe37e9e0 -- const char created with strdup: 21a9c8 -- const char created with GetStringUTFChars: 21aa28 -- After looking at this a little closer, all of the variable created have addresses which seem to be local to the function (in the fe... region as above). So it seems that GetStringUTFChars and strdup are pointing in similar regions, but how it relates to the Java Env is still unknown.
Bayou Bob
So, GetStringUTFChars seems to use the same heap of you... this complicates the demonstration. I don't know, maybe with some reverse engineering you may prove that Get/ReleaseStringUTFChars have some kind of internal reference counting scheme that link them to the JVM. Try to call twice GetStringUTFChars, and see if you get the same pointer: if you do, then probably GetStringUTFChars reference-count its returned values, and just freeing them is dangerous.
Matteo Italia
+1  A: 

What we finally discovered.

An application linking Java and C will share the heap space. This implies any memory allocated on the C side of things with malloc (et. al.) can not be allocated in the section of the heap reserved for Java. However, any memory allocated by the JNI functions themselves may or may not allocate space on the Java side of the heap (it being an implementation detail of JNI). This leaves two possibilities: 1) memory is allocated from the C side of the heap where calling free leaves no ill effects; 2) memory is allocated from the Java side will opens the possibility the memory is reclaimed by the GC before the C side is done with it. In the face of this uncertainty the safest course is to explicitly allocate new space with malloc, release the JNI memory, and call free whenever the newly allocated memory is no longer needed.

I'd like to thank Matteo Italia for his assistance in this.

Bayou Bob
Glad I've been useful. :)
Matteo Italia