I read on on IBM that
To access Java objects' fields and invoke their methods, native code must make calls to FindClass(), GetFieldID(), GetMethodId(), and GetStaticMethodID(). In the case of GetFieldID(), GetMethodID(), and GetStaticMethodID(), the IDs returned for a given class don't change for the lifetime of the JVM process. But the call to get the field or method can require significant work in the JVM, because fields and methods might have been inherited from superclasses, making the JVM walk up the class hierarchy to find them. Because the IDs are the same for a given class, you should look them up once and then reuse them. Similarly, looking up class objects can be expensive, so they should be cached as well.
How does one cache the methodID
, fieldID
, and class
objects in JNI?
Are there built-in methods or a specific procedure that has to be followed?
You can have some utility structures like this:
This is taken from my question: https://stackoverflow.com/questions/10617714/how-to-extend-swt-com-support
For some good examples look at the
os_structs.c
it is bundled with eclipse SWT implementation.Note: The above code is just an example and could be adapted for different OS. Also it just shows "how to access java fields"; for methods you could follow the same approach.
For simple case, I use static fields in the C code, initialized with an initId method called from my java classes :
And in C :
Here is how I practice IBM's recommendation. Considering demo java class like that:
The corresponding jni header file like that:
According to IBM's recommendation, we need to cache the used class
SimpleClazz
and the field id of the object membervalue
.After learning this good article, I cache the
SimpleClazz
in the functionJNI_OnLoad
, which is called when the native library is loaded (for example, through System.loadLibrary). In theJNI_Onload
, we do find class and store this jclass as a global field.Furthermore, in the native implementation of
getValue
, we use static local variable to cache the field id ofvalue
. This design is to make sure this filed id can be in the better scope, rather than in the global scope. The drawback of this design is that we need to compare with NULL each time we call this function. I learnt this design from section 4.4.1 of the book The Java Native Interface: Programmer's Guide and Specification.Finally, we also need to write the function
JNI_OnUnload
, which is called when the class loader containing the native library is garbage collected. In this function, we release the global reference of jclass.My cpp implementation is shown as below:
There is no built-in methodology to follow, however here is a somewhat standard, clean, and repeatable implementation showing how I practice IBM's recommendation.
I am going to assume you are calling your DLL from Java and you are referencing it multiple times throughout your application life-cycle.
The sample Native Java Class is named
org.stackoverflow.jni.NativeClazz
, which will implement the 2 built-in JNI methodsJNI_OnLoad()
andJNI_OnUnload()
.void JNI_OnLoad(JavaVM *vm, void *reserved): This method will be used to register the Class IDs as global variables and assign the Method IDs and Field IDs to static variables. The method is automatically called when the driver is loaded by the Java VM; it is only called once during the driver life-cycle.
void JNI_OnUnload(JavaVM *vm, void *reserved): This method will be used to free any global variables registered by
JNI_OnLoad()
. The VM will automatically callJNI_OnUnload()
immediately prior to application shutdown.Rationale: It's my understanding the Class IDs must be registered as global references to maintain the viability of any associated Method ID / Field IDs. If this isn't done and the class is unloaded from the JVM, on class reload, the Method IDs / Field IDs may be different. If the Class ID is registered as a global reference, the associated Method IDs and Field IDs do not need to be registered as global references. Registering a Class ID as a global reference prevents the associated Java class from unloading, therefore stabilizing the Method ID / Field ID values. Global references, including the Class ID,s should be removed in
JNI_OnUnload()
.Method IDs and Field IDs are not managed by the native code; they are managed by the virtual machine and are valid until the associated class is unloaded. Field IDs and Method IDs cannot be explicitly deleted before the virtual machine unloads the defining class; they can be left for the VM to handle after unload.
Sample Code
Comments in the following C++ code sections explain registering variables globally.
Here is the Java class
BeanObject
representing a data object:Here is a skeleton Java class
NativeClazz
:Here is the C++ header file generated using
javah
onNativeClazz
:Here is the C++ .cpp file implementing the header file: