JNI memory management using the Invocation API

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.

Asked by: Kristian938 | Posted: 23-01-2022

Answer 1

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.

Answered by: Ada759 | Posted: 24-02-2022

Answer 2

There are a couple of strategies for reclaiming native resources (objects, file descriptors, etc.)

  1. Invoke a JNI method during finalize() which frees the resource. Some people recommend against implementing finalize, and basically you can't really be sure that your native resource is ever freed. For resources such as memory this is probably not a problem, but if you have a file for example which needs to be flushed at a predictable time, finalize() probably not a good idea.

  2. Manually invoke a cleanup method. This is useful if you have a point in time where you know that the resource must be cleaned up. I used this method when I had a resource which had to be deallocated before unloading a DLL in the JNI code. In order to allow the DLL to later be reloaded, I had to be sure that the object was really deallocated before attempting to unload the DLL. Using only finalize(), I would not have gotten this guaranteed. This can be combined with (1) to allow the resource to be allocated either during finalize() or at the manually called cleanup method. (You probably need a canonical map of WeakReferences to track which objects needs to have their cleanup method invoked.)

  3. Supposedly the PhantomReference can be used to solve this problem as well, but I'm not sure exactly how such a solution would work.

Actually, I have to disagree with you on the JNI documentation. I find the JNI specification exceptionally clear on most of the important issues, even if the sections on managing local and global references could have been more elaborated.

Answered by: David193 | Posted: 24-02-2022

Answer 3

Re: "A separate, but related question"... you do not need to manually release jclass, jfieldID and jmethodID when you use them in a "local" context. Any actual object references you obtain (not jclass, jfieldID, jmethodID) should be released with DeleteLocalRef.

Answered by: Roman373 | Posted: 24-02-2022

Answer 4

The GC would collect your instance, but it will not automatically release the non-java heap memory allocated in the native code. You should have explicit method in your class to release the c_object instance.

This is one of the cases where I'd recommend using a finalizer checking if c_object has been released and release it, logging a message if it's not.

A useful technique is to create a Throwable instance in the Java class constructor and store it in a field (or just initialize the field inline). If the finalizer detects that the class has not been properly disposed it would print the stacktrace, pinpointing the allocation stack.

A suggestion is to avoid doing straight JNI and go with gluegen or Swig (both generate code and can be statically linked).

Answered by: Edgar129 | Posted: 24-02-2022

Similar questions

javascript - Session management in GWT without using Java on the server?

I am using GWT for my client side application. I am not using GWT/Java for the server. However, I am not sure how I can handle session management. The GWT application resides on one page, all server calls are done via AJAX. If a session expires on the server... let's assume the user didn't close the browser, but left the application open, how could my server notify the application that the session has expired and that ...

About Memory Management in Java and C++

Well, I've been given an assignment to basically figure out how memory allocation works for whatever language I'll be using. After some research, I have some questions and doubts which I'd like to get some insight in. For example: I read here that Java specifies exactly how the stack contents are organized. Looking at the

Management of Java string resources

I'm working on a java SE 1.5+ swing application, in conjunction with others. I'm wondering what the best way of managing string resources is. I understand the principles behind resource bundles etc. I'd like to avoid having one property file for every class that needs a string, as this seems a bit of overkill. Especially when you have a lot of classes that may only make a single reference to a string (say in an error handl...

Best practices for exception management in Java or C#

Closed. This question is opinion-based. It is not c...

memory management - Tell Java not to push an object into swap space

Is it possible tell the JVM to hint the OS that a certain object should preferably not get pushed out to swap space?

What are some Java memory management best practices?

Closed. This question is opinion-based. It is not c...

OpenGL, Java and memory management

When using openGL in Java (in bindings like jogl), do you have to worry about memory management? Does the JVM do garbage collection with the created openGL objects? If so, what's the best way to approach cleanup?

raii - Is C++ like resource management possible in Java

This question already has answers here:

memory management - Object Pooling in Java

What are the pro's and con's of maintaining a pool of frequently used objects and grab one from the pool instead of creating a new one. Something like string interning except that it will be possible for all class objects. For example it can be considered to be good since it saves gc time and object creation time. On the other hand it can be a synchronization bottleneck if used from multiple threads, demands explic...

java - How to deal with inept management

Closed. This question does not meet Stack Overflow guid...

Still can't find your answer? Check out these amazing Java communities for help...

Java Reddit Community | Java Help Reddit Community | Dev.to Java Community | Java Discord | Java Programmers (Facebook) | Java developers (Facebook)