This content has been marked as final. Show 4 replies
FYI, the crash seems to be occurring when executing Class.forName().
In C++ is relatively easy to write code which happens to work, but contains a bug never the less.
If the JVM fails only when you load your JNI, it is because the JNI is corrupting the memory.
It could be incorrectly handling Java Objects in a way which doesn't cause a problem on Solaris but does break on Linux x64. I would review how Objects are handled in the JNI layer.
This sounds like a garbage-collection issue, so I would make sure you have read and understood the sections of the JNI Specification starting with Referencing Java Objects, and make sure:
- your JNI code complies with that in every* respect
- it tests the result of every* JNI call via ExceptionOccurred(), ExceptionDescribe(), etc
- checks zero returns where necessary
and does not proceed blindly after an error or exception or zero return.
The JVM side of JNI doesn't do any checking at all, it is entirely up to you (e.g. that you don't pass zero as a method descriptor).
Thanks everyone. You guys pushed me to do my homework and do some heavy debugging. First I found the -Xcheck:jni option very useful.
I turns out quite a few things were wrong:
1. One static method was being invoked as member method, not static:
JNIEnv::CallLongMethod instead of JNIEnv::CallStaticLongMethod. Note that the program seemed to work just fine unil I turned on JNI checking.
2. The java class handles were not globalized even though they were invoked from different threads and from different procedures after the procedure that obtained the handel has exited. The solution was to "globalize" the handle before saving it off:
jclass clsid = myJNI_ENV->FindClass(QualifiedClassName);
/* error checking here */
jclass globid = (jclass)myJNI_ENV->NewGlobalRef(clsid);
3. The objects that were accessed across different methods were not globalized. Similar to the class ids, I added NewGlobalRef right after the creation:
jobject objid = myJNI_ENV->NewObject(classid, constructorMethodID
jobject globobjid = myJNIENV->NewGlobalRef(objid);
4. And finally, a multithreading issue. The java methods were being called from different threads. The application was coded in an attempt to handle that by calling
JavaVM->AttachCurrentThread((void **)&mpoJNI_ENV, NULL)
The problem was, detecting whether we were dealing with a new thread or not. Since the threads are being created completely outside our control in the middleware layer( Orbix, to be precise) we'd save and compare the thread IDs. The problem is, on linux, pthread_self() seems to always return the same ID even though we were dealing with an entirely different thread! We used linux-specific gettid() instead and it worked! The app stopped crashing and JNI check stopped complaining.