JNI: Difference between revisions
Line 24: | Line 24: | ||
== JNI and Threads == |
== JNI and Threads == |
||
=== Monitors === |
|||
;Entry and Exit |
|||
This block in java |
|||
<source lang="java"> |
|||
synchronized (obj) { |
|||
... // synchronized block |
|||
} |
|||
</source> |
|||
is equivalent to native code |
|||
<source lang="c"> |
|||
if ((*env)->MonitorEnter(env, obj) != JNI_OK) { |
|||
... /* error handling */ |
|||
} |
|||
... /* synchronized block */ |
|||
if ((*env)->MonitorExit(env, obj) != JNI_OK) { |
|||
... /* error handling */ |
|||
}; |
|||
</source> |
|||
<code>MonitorEnter</code> and <code>MonitorExit</code> work on <code>jclass</code>, <code>jstring</code>, <code>jarray</code> types. |
|||
;Wait and Notify |
|||
There are no particular JNI functions for these. Instead native code can simply call the corresponding methods in the Java API. Example for <code>wait()</code>: |
|||
<source lang="bash"> |
|||
/* precomputed method IDs */ |
|||
static jmethodID MID_Object_wait; /* same for _notify and ņotifyAll */ |
|||
void |
|||
JNU_MonitorWait(JNIEnv *env, jobject object, jlong timeout) |
|||
{ |
|||
(*env)->CallVoidMethod(env, object, MID_Object_wait, timeout); |
|||
} |
|||
</source> |
|||
=== Obtain the <code>JNIEnv</code> Pointer === |
=== Obtain the <code>JNIEnv</code> Pointer === |
||
Obtaining a reference to <code>JNIEnv</code> is necessary in order to issue JNI function calls. |
Obtaining a reference to <code>JNIEnv</code> is necessary in order to issue JNI function calls. |
Revision as of 15:10, 24 September 2011
This page is over JNI, The Java Native Interface of the java language. For other features, see the Java page.
References
- Sheng Liang, The Java Native Interace — Programmer's Guide and Specification, Addison Wesley[1].
Frequent Caveats
From [1]:
- Don't cache JNIEnv between threads
- Each thread gets its own value of JNIEnv
- Don't truncate jboolean arguments
- jboolean type is actually a 8-bit type, so value like 256/512 would actually be treated like false
- Don't mix thread models (green thread vs native thread) if models are different
- Cache field / Method ID at the time class is created
- Better performance, and avoid using the wrong field/method if a sub-class that overrides some field/method is passed.
- Use a same JNIEnv reference in different threads
- A JNIEnv pointer is only valid in the thread associated with it. Don't pass this pointer from one thread to another, or cache and use it in multiple threads.
- Pass local references to different threads
- Local referneces are valid only in the thread that created them. Instead convert local references to global reference whenever there is a possibility that multiple threads may use the same reference.
JNI and Threads
Monitors
- Entry and Exit
This block in java
synchronized (obj) {
... // synchronized block
}
is equivalent to native code
if ((*env)->MonitorEnter(env, obj) != JNI_OK) {
... /* error handling */
}
... /* synchronized block */
if ((*env)->MonitorExit(env, obj) != JNI_OK) {
... /* error handling */
};
MonitorEnter
and MonitorExit
work on jclass
, jstring
, jarray
types.
- Wait and Notify
There are no particular JNI functions for these. Instead native code can simply call the corresponding methods in the Java API. Example for wait()
:
/* precomputed method IDs */
static jmethodID MID_Object_wait; /* same for _notify and ņotifyAll */
void
JNU_MonitorWait(JNIEnv *env, jobject object, jlong timeout)
{
(*env)->CallVoidMethod(env, object, MID_Object_wait, timeout);
}
Obtain the JNIEnv
Pointer
Obtaining a reference to JNIEnv
is necessary in order to issue JNI function calls.
For native methods called from the JVM, this is easy since the the JNIEnv
reference for the current thread is always passed as a parameter to the call.
In other circumstance, one must first call AttachCurrentThread
to associate the current thread to an existing JVM. If the current native thread is not yet associated, AttachCurrentThead
will create a new java.long.instance
in the JVM, and returns the corresponding JNIEnv
reference. Attached thread can then issue JNI function calls. Use DetachCurrentThread
if the current thread does not need to make native calls anymore (doing so will enable clean-up and freeing resources).
Since Java 2 SDK release 1.2, a new method GetEnv
allows to test whether the current thread is attached to a given VM instance, and to return the JNIEnv
reference if it is the case. Note that GetEnv
and AttachCurrentThread
are functionally equivalent if the current thread is already attached to the VM.
Registering Native Methods
A given JNI method declared in java must be associated to a native method implementation before it is possible to call that method. This is the purpose of the native method registration.
The standard method is to rely on the standard java 2-step mechanism:
System.loadLibrary
to locate and load a named native library.- The VM locates the native method implementation in one of the loaded native libraries (e.g.
Foo.g()
requires locating and linking the native methodJava_Foo_g
Another method is to do the association manually:
void JNICALL g_impl(JNIEnv *env, jobject self);
//...
JNINativeMethod nm;
nm.name = "g";
/* method descriptor assigned to signature field */
nm.signature = "()V";
nm.fmPtr = g_impl;
(*env)->RegisterNatives(env, cls, &nm, 1);
The advantage of the manual method:
- The standard method is slow (lazy).
- The manual method allow to update the native method implementation at runtime.
- If the VM is launched by a native application,
RegisterNatives
allows to register native methods of that application, whereas the standard method only search native implementation in native libraries, not in the application itself.