How to free up jstring memory allocated by env->NewString (jchar*, length)

3,293 views
Skip to first unread message

Elvis Dowson

unread,
Jul 4, 2009, 3:21:16 AM7/4/09
to android-ndk
Hi,
If I allocate a jstring by calling env->NewString(jchar* ,
length) , what should I do to clean up the memory. I get a JVM crash
after a few calls.

Is the correct approach to call env->ReleaseStringChars(jstring,
jchar)?

static void nativeCallbackS0(int id,
uint8_t * pIncomingDataBuffer,
int length,
JNIEnv* env,
jclass clazz)
{

jchar tempChars[length];
for (int i = 0; i < length; i++) {
tempChars[i] = pIncomingDataBuffer[i];
}

// TODO: Check for memory leaks
jstring result = env->NewString(tempChars, length);
if (result == NULL) {
LOGE("NewString failed\n");
return;
}

// Call serial callback- this is a java method that is being
called from this c method
env->CallStaticVoidMethod(clazz, gCachedState.callback, result);

// Question: Is this the correct way to free the jstring
memory
env->ReleaseStringChars(result, tempChars);
}


Elvis


W/dalvikvm( 1069): JNI WARNING: threadid=5 using env from threadid=3
W/dalvikvm( 1069): in Ldalvik/system/NativeStart;.run ()V
(NewString)
I/dalvikvm( 1069): "HeapWorker" daemon prio=5 tid=5 VMWAIT
I/dalvikvm( 1069): | group="system" sCount=0 dsCount=0 s=0
obj=0x427f7128
I/dalvikvm( 1069): | sysTid=1071 nice=0 sched=0/0 handle=1308216
I/dalvikvm( 1069): at dalvik.system.NativeStart.run(Native Method)
I/dalvikvm( 1069):
E/dalvikvm( 1069): VM aborting
I/DEBUG ( 901): *** *** *** *** *** *** *** *** *** *** *** *** ***
*** *** ***
I/DEBUG ( 901): Build fingerprint: 'generic/generic/generic/:1.5/
CUPCAKE/eng.elvis.20090627.224417:eng/test-keys'
I/DEBUG ( 901): pid: 1069, tid: 1071 >>>
com.energyhub.serial_library.client <<<
I/DEBUG ( 901): signal 11 (SIGSEGV), fault addr deadd00d
I/DEBUG ( 901): r0 00000328 r1 0000000c r2 0000000c r3 00000026
I/DEBUG ( 901): r4 deadd00d r5 00000000 r6 ad083e10 r7 00000000
I/DEBUG ( 901): r8 bef64600 r9 afe39dd0 10 ad046f19 fp 00000001
I/DEBUG ( 901): ip ad083eec sp 100ff9d8 lr afe13e8d pc
ad03b5c2 cpsr 20000030
I/SerialLibrary( 1069): s: 0042
l Tester #2, my timer i
D/SerialLibrary( 1069): Callback:
D/SerialLibrary( 1069): 2400 :s
I/DEBUG ( 901): #00 pc 0003b5c2 /system/lib/libdvm.so
I/DEBUG ( 901): #01 pc 0002deae /system/lib/libdvm.so
I/DEBUG ( 901): #02 pc 0002e2b4 /system/lib/libdvm.so
I/DEBUG ( 901): #03 pc 0002e992 /system/lib/libdvm.so
I/DEBUG ( 901): #04 pc 00001086 /system/lib/
libserial_library_jni.so
I/DEBUG ( 901): #05 pc 00001128 /system/lib/
libserial_library_jni.so
I/DEBUG ( 901): stack:
I/DEBUG ( 901): 100ff998 00000005
I/DEBUG ( 901): 100ff99c 00000001
I/DEBUG ( 901): 100ff9a0 afe39f90 /system/lib/libc.so
I/DEBUG ( 901): 100ff9a4 afe39fe4 /system/lib/libc.so
I/DEBUG ( 901): 100ff9a8 00000000
I/DEBUG ( 901): 100ff9ac afe13e8d /system/lib/libc.so
I/DEBUG ( 901): 100ff9b0 0016b160 [heap]
I/DEBUG ( 901): 100ff9b4 afe12ea9 /system/lib/libc.so
I/DEBUG ( 901): 100ff9b8 00000000
I/DEBUG ( 901): 100ff9bc ad083e10 /system/lib/libdvm.so
I/DEBUG ( 901): 100ff9c0 00000000
I/DEBUG ( 901): 100ff9c4 ad083e10 /system/lib/libdvm.so
I/DEBUG ( 901): 100ff9c8 00000000
I/DEBUG ( 901): 100ff9cc afe12f0d /system/lib/libc.so
I/DEBUG ( 901): 100ff9d0 df002777
I/DEBUG ( 901): 100ff9d4 e3a070ad
I/DEBUG ( 901): #00 100ff9d8 00000000
I/DEBUG ( 901): 100ff9dc ad02deb3 /system/lib/libdvm.so
I/DEBUG ( 901): #01 100ff9e0 00000000
I/DEBUG ( 901): 100ff9e4 ad02e2b9 /system/lib/libdvm.so
D/Zygote ( 902): Process 1069 binder: 918:1046 transaction failed
29189, size52-0
terminated by signal (11)

niko20

unread,
Jul 5, 2009, 1:09:42 PM7/5/09
to android-ndk
Hi,

I'm not sure what is happening in your case, but from that JNI docs
I've read, if you allocate memory that ever gets passed back to the
java layer, then the java layer will take care of freeing it.

-niko

John Ripley

unread,
Jul 6, 2009, 11:55:28 AM7/6/09
to andro...@googlegroups.com
You're creating a new Java string when you call NewString(), copying the contents of the buffer you supplied. This returns a local object reference, which you can either leave to be cleaned up on returning from the native call, or preemptively clean up with DeleteLocalRef().

Your call to ReleaseStringChars() is what you would do if you obtained a pointer to an existing Java string with GetStringChars()..

2009/7/4 Elvis Dowson <elvis....@gmail.com>

fadden

unread,
Jul 6, 2009, 2:22:19 PM7/6/09
to android-ndk
On Jul 4, 12:21 am, Elvis Dowson <elvis.dow...@gmail.com> wrote:
>       If I allocate a jstring by calling env->NewString(jchar* ,
> length) , what should I do to clean up the memory. I get a JVM crash
> after a few calls.

The same thing you'd do if you wrote "String blah = new String()",
i.e. let the garbage collector deal with it.

The only thing you may need to do is tell the VM that your native code
no longer needs it. You do that by deleting the local reference
returned by NewString, using DeleteLocalRef. (This will happen
automatically if this is a function that returns to code written in
Java.) See the JNI Tips document in dalvik/docs/jni-tips.html for for
information about local and global references. Also available on the
web from git:

http://android.git.kernel.org/?p=platform/dalvik.git;a=blob_plain;f=docs/jni-tips.html;hb=HEAD

> W/dalvikvm( 1069): JNI WARNING: threadid=5 using env from threadid=3
> W/dalvikvm( 1069):              in Ldalvik/system/NativeStart;.run ()V
> (NewString)
> I/dalvikvm( 1069): "HeapWorker" daemon prio=5 tid=5 VMWAIT
> I/dalvikvm( 1069):   | group="system" sCount=0 dsCount=0 s=0
> obj=0x427f7128
> I/dalvikvm( 1069):   | sysTid=1071 nice=0 sched=0/0 handle=1308216
> I/dalvikvm( 1069):   at dalvik.system.NativeStart.run(Native Method)

Well, that's a new one. Somehow the JNIEnv is being shared between
threadid=3 (the main thread) and threadid=5 (the HeapWorker or
"finalizer" thread). Except that doesn't really make sense, since the
HeapWorker stack trace doesn't show any activity. Maybe the thread-
local storage got tangled up?

At any rate, you're not running out of memory.

Elvis Dowson

unread,
Jul 11, 2009, 9:48:51 AM7/11/09
to android-ndk
Hi,

On Jul 6, 10:22 pm, fadden <fad...@android.com> wrote:
>
> Well, that's a new one.  Somehow the JNIEnv is being shared between
> threadid=3 (the main thread) and threadid=5 (the HeapWorker or
> "finalizer" thread).  Except that doesn't really make sense, since the
> HeapWorker stack trace doesn't show any activity.  Maybe the thread-
> local storage got tangled up?

I just read the android jni tips located here :
and here is a quote from that document:

On some VMs, the JNIEnv is used for thread-local storage. For this
reason, you cannot share a JNIEnv between threads. If a piece of code
has no other way to get its JNIEnv, you should share the JavaVM, and
use JavaVM->GetEnv to discover the thread's JNIEnv.

At the moment, I am caching the JNIEnv obtained from a device.open()
method in the device client.java project. Then this JNIEnv pointer is
being cached in the device c callback method, and each time the
callback gets invoked when some new data is available, I am using the
JNIEnv pointer to invoke the java callback to notify the device.java
component that some data is available to be read and handled.

So, after 4 or 5 calls, the garbage collector gets invoked, and then
the system crashes.

Does invocation of the garbage collector somehow change the JNIEnv
pointer?

I've posted a separate thread to find out how to obtain the JavaVM in
my jni cpp code, so that I can use that to obtain the JNIEnv pointer
each time the jni cpp callback needs to invoke the java callback
method.

Best regards,

Elvis

fadden

unread,
Jul 13, 2009, 2:20:13 PM7/13/09
to android-ndk
On Jul 11, 6:48 am, Elvis Dowson <elvis.dow...@gmail.com> wrote:
> Does invocation of the garbage collector somehow change the JNIEnv
> pointer?

No. It's created when the thread is created or attached, and tucked
into the pthread's thread-local storage (TLS). If you have CheckJNI
enabled, the JNIEnv* you pass in is compared against the value in TLS,
and an error is thrown if they don't match (see above). If you don't
have CheckJNI enabled, the check is skipped, and things can get
*really* weird if you mix and match. :-)

When you make a vm->GetEnv() call, it pulls the value out of TLS.

> I've posted a separate thread to find out how to obtain the JavaVM in
> my jni cpp code, so that I can use that to obtain the JNIEnv pointer
> each time the jni cpp callback needs to invoke the java callback
> method.

That'll ensure your JNIEnv* don't get tangled up. The failure mode is
still a little strange looking.
Reply all
Reply to author
Forward
0 new messages