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.
There are a couple of strategies for reclaiming native resources (objects, file descriptors, etc.)
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.
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.)
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.
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.
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).
Related
As stated in the answer to this question,
All jobjects created inside a JNI method are local references by default. Whenever a JNI method returns, all local references are automatically released.
In the same time,
When a global reference is no longer needed, it should be deleted by DeleteGlobalRef, otherwise referenced object will never be garbage-collected.
So, in order to return a jobject from JNI back to Java, we need global reference. But in the same time, how can we make it to be eventually garbage-collected on Java side, if DeleteGlobalRef cannot be called from there?
I get the gist of reference objects in Java, and the basic differences between soft, weak, and phantom reference objects.
However, I don't fully understand the following points from the API docs
From the API doc for WeakReference<T>:
"Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed."
Now, the terms in bold haven't been explained anywhere in the API docs, so I wonder what they precisely mean, especially in relation to the more or less deprecated Object.finalize() method's notion of finalization.
From the API doc for Reference<T>:
public void clear(): "This method is invoked only by Java code; when the garbage collector clears references it does so directly, without invoking this method."
public boolean enqueue(): "This method is invoked only by Java code; when the garbage collector enqueues references it does so directly, without invoking this method."
Again, I don't know what is meant by "Java code" in above 2 quotes: The JVM internal code to which I have no access? Or, the JDK code to which I have readonly/browsing access? Or, the end-user's own Java code?
The "directly, without invoking this method" part tells me that JVM has no need to call these methods. On the other hand, the "only by Java code" part tells me that it is not the end-user's Java code but rather the JVM's (if it meant end-user code, then we'd be finding this phrase littered in all of the API doc for almost every method of every Java class!). So which interpretation is right and who can call this function?
"Weak reference objects, which do not prevent their referents from being made finalizable, finalized, and then reclaimed."
These are all stages in the Garbage Collection process. Objects first get marked as finalizable to say that there are no strong references to them. Then finalize() is called and they are marked as finalized, and then finally the memory is reclaimed.
public void clear(): "This method is invoked only by Java code; when the garbage collector clears references it does so directly, without invoking this method."
This is saying that when you as a programmer decide to clear a reference then the clear() method is used to do that, however if you were to subclass WeakReference and override the clear method you would NOT see the JVM calling that method when the object was removed.
The quote for enqueue is essentially saying the same thing. It is a warning that you cannot interact with the workings of the GC by overriding these methods.
"finalizable, finalized, and then reclaimed." means garbage collected.
"only by java code" means called from your program itself (including the JDK) - i.e. you may have some code somewhere that calls ref.clear();. It also explains that the GC (i.e. the JVM) does effectively clear the reference but with a different mechanism that does not call the clear method. For example, if you override clear to be a no-op, the GC will still be able to "nullify" the reference.
1) I have a native java function which passes several params and its implementation is a native C++ constructor to create an object and returns a long which is cast from the pointer to object. This object's constructed members are effectively immutable. The C++ object then can do work based on its constructed state.
2) java code that gets the result of the function call safely publishes the longified version of the pointer somewhere (without mutex) and changes a volatile variable to hopefully publish the memory changes in the native C++ object to other threads
Now another thread reads that volatile variable of 2), and then picks up that published long, and calls another native function that accesses that effectively immutable object in the C++ memory space to do some work.
Question: Is that other thread guaranteed to see the fully constructed native object because of the Java Memory Model guarantees about volatiles and fences? I would bet the answer is yes on some platforms, but I see that different chips work in different ways with using fences, and was wondering about all platforms for which java is available.
A co-author of JCIP answered the question on the JSR mailing list for concurrency.
He says: "JMM [makes no] guarantees extending to anything outside the Java heap - or more specifically it only applies to Java fields", but that "In practice the barriers/fences used today are coarse-grained and will affect all memory equally", so "that in practice this [the volatile publishing attempt described in the question] will work fine (as long as you are using normal process memory)".
Another respondent on the mailing list says: "Some of us have definitely considered it a goal to ensure that Java, C, and C++ synchronization works together as expected, with Java synchronization providing the right visibility guarantees for C or C++ variables, and conversely" but adds that "there is no written guarantee of that [behavior]".
What you pass from java code to native code is copied and passed in the appropriate datatypes for the native code (for example C++ has unsigned int while Java does not, null terminated strings etc).
After that, any threading issues are related to the implementation of the native code.
Essentially it is the same an API. What are the contracts for the API? Does it specify that it can be accessed safely by multiple threads or not?
Java doesn't even know about the existence of the native object you created in your C code, so how can its memory model possibly provide any guarantees about it? All that Java knows about, and can guarantee the behavior of, is the volatile long.
We have the concept of pointers in C++. Now if we allocate some memory in C++ and pass it on to Java as an object reference(using JNI) then who should be and who will be freeing it.
Will it be
1.)The Garbage collector does it automatically in Java?
2.)We need to explicitly do a delete on the pointer in the wrapped JNI class finalize method?
3.)Or we should just forget finalize(as finalizers cannot be trusted) and it is responsibility of Java to call a C++ code which deletes the object
4.)Or is there some way to deallocate the memory directly in Java itself (not sure how Java intreprets a C++ pointer inorder to delete it)?
What is the best practice for doing this and vice versa(when we pass objects from Java to C++)?
We have the concept of pointers in C++. Now if we allocate some memory in C++ and pass it on to Java as an object reference(using JNI) then who should be and who will be freeing it.
The best strategy is usually to have the allocator also be the one to free the data.
1.)The Garbage collector does it automatically in Java?
The problem with this is you don't know when, if ever it will run.
2.)We need to explicitly do a delete on the pointer in the wrapped JNI class finalize method?
Better to have a release() method in Java rather than imply that C++ has to delete it. You may want C++ to recycle the memory.
3.)Or we should just forget finalize(as finalizers cannot be trusted) and it is responsibility of Java to call a C++ code which deletes the object
If you mean, allocate the memory in Java and pass it to C++ to populate. This is my preference.
I would use can use ByteBuffer.allocateDirect() and you can call ((DirectBuffer) buffer).cleaner().clean(); to clean it up deterministically.
This can make recycling the memory simpler, possibly the same buffer can be used for the life of the application.
I'm working on wrapping a C DLL library to Java using JNA. The library has provided a C# wrapper. In the constructor of C# wrapper, a object is created and the memory of the object is pinned by
this.m_object = _CreateObject();
this.m_objectGCH = GCHandle.Alloc(this.m_object, GCHandleType.Pinned);
m_object is an integer pointing to the created object, and the memory of the object is pinned by GCHandle.Alloc(). I can create a object and get the pointer to the object by JNA. However, I have no idea to pin the object memory in Java.
Java's GC has no awareness of the native memory allocated for your object. If you are responsible for deleting the memory at some future point, you must do so explicitly in your Java code by calling whatever "free" method is recommended by your object allocation.
If you need to ensure that Java does not GC a given Java object, then you need to ensure there is a reference to it until you no longer need it (the easiest way to do so is by storing it in a static (class) variable).