When I'm building a java object using JNI methods, in order to pass it in as a parameter to a java method I'm invoking using the JNI invocation API, how do I manage its memory?
Here's what I am working with:
I have a C object that has a destructor method that is more complex that free()
. This C object is to be associated with a Java object, and once the application is finished with the Java object, I have no more need for the C object.
I am creating the Java object like so (error checking elided for clarity):
c_object = c_object_create ();
class = (*env)->FindClass (env, "my.class.name");
constructor = (*env)->GetMethodID (env, class, "<init>", "(J)V");
instance = (*env)->NewObject (env, class, constructor, (jlong) c_object);
method = (*env)->GetMethodID (env, other_class, "doSomeWork", "(Lmy.class.name)V");
(*env)->CallVoidMethod (env, other_class, method, instance);
So, now that I'm done with instance
, what do I do with it? Ideally, I'd like to leave the garbage collection up to the VM; when it's done with instance
it would be fantastic if it also called c_object_destroy()
on the pointer I provided to it. Is this possible?
A separate, but related question has to do with the scope of Java entities that I create in a method like this; do I have to manually release, say, class
, constructor
, or method
above? The JNI doc is frustratingly vague (in my judgement) on the subject of proper memory management.
The JNI spec covers the issue of who "owns" Java objects created in JNI methods here. You need to distinguish between local and global references.
When the JVM makes a JNI call out to native code, it sets up a registry to keep track of all objects created during the call. Any object created during the native call (i.e. returned from a JNI interface function) are added to this registry. References to such objects are known as local references. When the native method returns to the JVM, all local references created during the native method call are destroyed. If you're making calls back into the JVM during a native method call, the local reference will still be alive when control returns back to the native method. If the JVM invoked from native code makes another call back into the native code, a new registry of local references is created, and the same rules apply.
(In fact, you can implement you're own JVM executable (i.e. java.exe) using the JNI interface, by creating a JVM (thereby receiving a JNIEnv * pointer), looking up the class given on the command line, and invoking the main() method on it.)
All references returned from JNI interface methods are local. This means that under normal circumstances you do not need to manually deallocate references return by JNI methods, since they are destroyed when returning to the JVM. Sometimes you still want to destroy them "prematurely", for example when you lots of local references which you want to delete before returning to the JVM.
Global references are created (from local references) by using the NewGlobalRef(). They are added to a special registry and have to be deallocated manually. Global references are only used for Java object which the native code needs to hold a reference to across multiple JNI calls, for example if you have native code triggering events which should be propagated back to Java. In that case, the JNI code needs to store a reference to a Java object which is to receive the event.
Hope this clarifies the memory management issue a little bit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With