Lifetime of JNI objects between calls.

Is it possible to store/cache non-class JNI objects between calls?

I know I can 'lock' jclass object to *env however I need to be able to do more than this.

Implementation :

C++ code for which I am not responsible for NOR will I ever get access to the source code needs to be built into a Java Application.

Everything that I need to do works fine except when I need to have jobject's and, in particular, the JNIEnv reference to be cached in the dll between JNI calls.

Raison d'etre : I want to implement a Callback using a Java object (Observer/Observable) so that the status of the C legacy app can be monitored via a callback implemented as part of the dll.

Java object is called via the C callback, sets status via a status method called via JNI, Java App is notifoed of the change.

Problem, as long as the first call the the jobject occurs within the actual JNI call that creates the Java object all is well, as soon as I try and store the jobject and JNIEnv references so that the C callback can call it as well then things break.

Is it possible to have JNI objects whose lifetime spans JNI calls at all?

[1175 byte] By [andyba] at [2007-9-26 1:41:33]
# 1
I haven't needed them for a while, but I believe you can solve your problem using NewGlobalRef. This JNI method tells java to keep the referenced object alive. Sooner or later you will have to destroy the reference ourself.
bschauwe at 2007-6-29 2:33:18 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 2

As bschauwe says, you can use global references to hang on to the Java object between method invocations. FWIW, when we've tried to do this sort of thing on Win32 with the Microsoft JVM, we've had problems and ended up just polling the DLL for the events. Your mileage may vary and all that sort of thing...

Yours,

Tom

tcopeland at 2007-6-29 2:33:18 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 3

Ahh, one of the things skipped over in the Tutorial that I needed. Great stuff thanks guys.

I am not surprised that this doesn't work very well with the Microsoft JVM, the thing is buggy as hell and still only guarantees JDK1.1.4 compat.

Luckily(?) I am using HotSpot.

Again, thanks and I will post here how things work out.

andyba at 2007-6-29 2:33:18 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 4

Ok, things are not working as I would like however...

The problem seems to be that I need to extend the lifetime of the *env between JNI calls. ie

call from Java->JNI method sets up a callback object and returns it to the calling app.

CCallback object caches the Java callback class and instantiated object.

OnSomethingHasChanged in the CCallback causes a call to the setState method of the java callback class using the env->GetMethodID(jCallbackClass, "setState", "(I)V")

and env->CallVoidMethod(jCallback, methodID, state);

which is the callback.

The VM crashes in a snotty heap, most likely due to a stale reference to env.

Assume that I have created global ref's for the callback object and its class.

I want to avoid having to invoke a JVM from the DLL which would mean

Java -> JNI -> DLL -> JNI ->Java as this application has to run in a web application.

I also want to avoid having to poll the DLL as I need to implement event handlers however if I must then I must.

There is another issue of course, Threading.

The callback created is created in one thread but its setState method is called in another. I have yet to educate myself fully in this matter but any enlightenment would be appreciated.

Andy

andyba at 2007-6-29 2:33:18 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 5
Not sure this is exactly what you are looking for, but: Our experience is specifically about creating a java object - via JNI - in one C thread, then accessing it later - via JNI - from another C thread. Bad things happen then. So we avoid the practice.
bschauwe at 2007-6-29 2:33:18 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 6
True enough. andyba, do you have Liang's JNI book? I vaguely recall it having a good discussion of this very topic. Yours,Tom
tcopeland at 2007-6-29 2:33:18 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 7

In the meantime I have found out exactly how to achieve callbacks via native/Java. Having studied Sheng Liang's book (an absolute must buy and read) things became a lot clearer.

This is what I discovered...

The main issues with implementing callbacks within a JNI framework are the following..

Lifetime of the JNIEnv and other classes/objects involved on the native side of the process.

Threading issues on the native side, especially when interacting with Objects created in a different thread.

The first issue is dealt with, in part, by being able to obtain a valid JNIEnv at arbitrary points in the native code. If you obtain a valid JNIEnv in an arbitrary native thread that JNIEnv is valid in it so this also helps problems with the second issue.

The key to obtaining a valid JNIEnv is in having a reference to the calling VM.

The function..

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)

allows you to cache a reference to the calling JVM. It is called when the dll is loaded before any other functions in the native code are called. The JavaVM reference is valid throughout the lifetime of the dll's instance.

you can then do this to obtain a valid JNIEnv anytime you like..

JNIEnv *env;

vm->GetEnv((void **)&env, JNI_VERSION_1_2);

Ok, thats dealt with problem of obtaining valid JNIEnv's when we need them.

In order to extend the lifetime of objects and, in particular, classes from which those objects are instantiated you need to create global ref's to them. I did mine this way..

In the calling Java code, I instantiate a callback object and pass that to a native method whose native code does the following..

gets the object's class as a local reference, creates a NewGlobalRef of the passed object and its class. These jobject and jclass global references are stored as global vars in the DLL. You can also, at this point, create jmethodID's for all the methods you may need to call as part of the callback process however callbacks should finish as quickly as possible so restrict your calls to the minimum. Caching the method ID does not require a global ref, nor is is thread 'sensitive'. GetMethodID can be used to store a local jmethodID reference in a global jmethodID var, this saves you some time and performance hits in the native callback functions.

The above description deals with the lifetime of objects between JNI calls. What about the threading issues?

We have seen how to get a valid JNIEnv when we want one. This JNIEnv is valid for the NATIVE thread in which we call vm->GetEnv((void **)&env, JNI_VERSION_1_2);

There is another way of getting valid JNIEnv's when we want them but this seems only to work when we are using the invocation interface (having the native side create an embedded JVM). This is the jvm->AttachCurrentThread function. I have tried both, only GetEnv seems to work as expected in the native DLL code, AttachCurrentThread when the JVM is invoked from inside a native application.

To successfully perform the native/Java callback, assuming you have cached a JavaVM reference and the callback object/class correctly, is to get a valid JNIEnv as shown above, use this JNIEnv for the method calls you need to perform on the callback object, cleanup if needed.

Note, you MUST DeleteGlobalRef's before the DLL is unloaded. If you don't you may find your Java VM crashing on exit (a horrible bug to have to track down).

Either explicitly create a cleanup native method that you have to call and/or use the JNI_OnUnload function that is called just before the DLL is unloaded (usually when the JVM is being stopped).

Things you can do to make your callback efficient :

Make the calls return as soon as possible. I use the Observable/Observer pattern so that further Java objects (the observers) can handle the changes made to the callback object. If, like me, you are reflecting state changes in the native system component via callbacks and displaying them in a GUI environment then your Observer can wake up a thread to do the GUI updates, the observable callback need only notifyObservers in order to start things off and return.

Hope this clears things up for you, if you have further questions please mail me, I believe my email address is available, if not post here and I will get to you.

andyba at 2007-6-29 2:33:18 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 8

Hi! I also have a problem like yours. I also need to have a callback function since the c library/api that I will be using returns values that way so I dont have a choice but also call my java code that way(callback style). I read all your posted message and all the replies but I still do not understand everything. I believe that your application runs on Microsoft. I am developing under Solaris. Do you think the same solution will work for solaris too? I had only 1 JNI programming experience before and it wasn't as complex as this. Is it possible for you to send me a snippet/part of the code where the references are made global and the passing of callBack object and the calling it also. I really, really need your help. We only have one JNI book and it only discussed about the "env->NewGlobalRef(obj)" but when I try to use this, an error of "request for member NewGlobalRef in something not a structure or union" Please help me. If you dont want to post the snippet here, please send it to me thru email. My email address is: emonette@ntsp.nec.co.jp

Thank you So Much :-)

Monette

mon03 at 2007-6-29 2:33:18 > top of Java-index,Java HotSpot Virtual Machine,Specifications...
# 9

Hello,

This post I have found extreemly helpful. But I am running into a problem that I cannot resolve as of this time.

I have a gui Java program which loads my JNI dll to be able to access c api calls. I have setup a callback system as you described.

I create a thread which waits for data (in the JNI c layer). In this thread I then call back the Native Java object as necessary.

I have found that I cannot use vm->GetEnv otherwise any env calls crash. I have found that AttachCurrentThread does seem to work ok.

But this is the problem.

The first time the callback is called, it works fine. The Java Object method is called, data is passed and everything is great.

The next call to the callback object fails. I am using JDK 1.3.1_06.

Any help would be very much appreciated.

Thanks

Pete Geremia

pgeremia at 2007-6-29 2:33:18 > top of Java-index,Java HotSpot Virtual Machine,Specifications...