The JNI in a Nutshell


As set forth in [1], the JNI enables Java code running on a JVM to work in concert with code written in other programming languages (e.g., C, C++, or assembly, referred to as “native languages” due to their “nativeness” to the execution environment). The JNI can be handy to foster code reuse or to implement mission-critical parts of the code in a native language for superior efficiency. The JNI may also occasionally be useful to facilitate platform-dependent features not available through the standard Java class library. The JNI provides bidirectional cooperation between Java code and native code so that Java methods may transparently invoke native routines, and vice versa. Additional functional wealth available to native applications includes manipulating Java objects, access to Java class information and run-time type checking facilities, dispatching Java exceptions, as well as convenient usage of Java thread synchronization mechanisms. Finally, the so-called Invocation API allows any native application to directly operate the JVM as a regular native object.

Every native function receives a JNI interface pointer through which it calls all the other JNI functions. For the sake of implementation flexibility, the interface pointer indirectly points to a table of pointers to JNI functions. Therefore, calling a native method always entails several dereference operations. Note also that the interface pointer is valid only in the current thread. On the Java side, this pointer is implicitly defined as part of the signature of a native method; on the native-language side, this pointer is explicitly defined as part of the signature and constitutes the first parameter. The JNI also prescribes the meaning of the second parameter to native methods. This parameter contains a reference to the host object (this) for instance methods (non-static functions) and a reference to the Java class object for class methods (static functions). Libraries of native functions are normally loaded dynamically at run time, using the System.loadLibrary method. Name mangling conventions for native methods allow overloading and are stipulated by the JNI Specification.

The JNI allows native code to access Java objects of both primitive types (e.g., int, char) and user-defined types. The JNI Specification associates each Java primitive type with an equivalent native type (for instance, the jfloat C++ native type corresponds to the Java float and is implemented as a 32-bit variable). Native methods may receive Java objects as parameters. Retrieving data members of a compound parameter (including individual array entries) or creating new objects in the Java environment, is performed by calling appropriate JNI functions. The JVM keeps track of all the objects made available to the native code so that they do not get garbage-collected while in use. Calling Java methods and raising exceptions from the native code can also be accomplished through a variety of JNI functions.