This section describes how Jikes RVM interfaces to native code. There are three major aspects of this support:
- JNI Functions: This is the mechanism for transitioning from native code into Java code. Jikes RVM implements the 1.1 through 1.4 JNI specifications.
- Native methods: This is the mechanism for transitioning from Java code to native code. In addition to the normal mechanism used to invoke a native method, Jikes RVM also supports a more restricted syscall mechanism that is used internally by low-level VM code to invoke native code.
- Integration with threading: JNI may be freely used from any Java method. The mechanisms required to make this work are discussed in great detail in Thread Management, and to some extent in the sections that follow.
All of the 1.1 through 1.4
JNIEnv interface functions are implemented.
The functions are defined in the class
JNIFunctions. Methods of this class are compiled with special prologues/epilogues that translate from native calling conventions to Java calling conventions and handle other details of the transition related to threading. Currently the optimizing compiler does not support these specialized prologue/epilogue sequences so all methods in this class are baseline compiled. The prologue/epilogue sequences are actually generated by the platform-specific
Calling a JNI function results in the thread attempting to transition from IN_JNI to IN_JAVA using a compare-and-swap; if this fails, the thread may block to acknowledge a handshake. See Thread Management for more details.
Invoking Native Methods
There are two mechanisms whereby RVM may transition from Java code to native code.
The first mechanism is when RVM calls a method of the class
SysCall. The native methods thus invoked are defined in one of the C and C++ files of the
JikesRVM executable. These native methods are non-blocking system calls or C library services. To implement a syscall, the RVM compilers generate a call sequence consistent with the platform's underlying calling convention. A syscall is not a GC-safe point, so syscalls may modify the Java heap (eg. memcpy()). For more details on the mechanics of adding a new syscall to the system, see the header comments of SysCall.java. Note again that the syscall methods are NOT JNI methods, but an independent (more efficient) interface that is specific to Jikes RVM.
The second mechanism is JNI. Naturally, the user writes JNI code using the JNI interface. RVM implements a call to a native method by using the platform-specific
JNICompiler to generate a stub routine that manages the transition between Java bytecode and native code. A JNI call is a GC-safe point, since JNI code cannot freely modify the Java heap.
Interactions with Threading
See the Thread Management subsection for more details on the thread system in Jikes RVM.
There are two ways to execute native code: syscalls and JNI. A Java thread that calls native code by either mechanism will never be preempted by Jikes RVM, but in the case of JNI, all of the VM's services will know that the thread is "effectively safe" and thus may be ignored for most purposes. Additionally, threads executing JNI code may have handshake actions performed by other threads on their behalf, for example in the case of GC stack scanning. This is not the case with syscalls. As far as Jikes RVM is concerned, a Java thread that enters syscall native code is still executing Java code, but will appear to not reach a safe point until after it emerges from the syscall. This issue may be side-stepped by using the
leaveNative methods, as shown in
- Native Libraries: JNI 1.2 requires that the VM specially treat native libraries that contain exported functions named JNI_OnLoad and JNI_OnUnload. Only JNI_OnLoad is currently implemented.
- JNICompiler: The only known deficiency in JNICompiler is that the prologue and epilogues only handle passing local references to functions that expect a jobject; they will not properly handle a jweak or a regular global reference. This would be fairly easy to implement.
- JavaVM interface: The JavaVM interface has GetEnv fully implemented and AttachCurrentThread partly implemented, but DestroyJavaVM, DetachCurrentThread, and AttachCurrentThreadAsDaemon are just stubbed out and return error codes. There is no good reason why AttachCurrentThread and friends cannot be implemented; it just hasn't been done yet, mostly because there was no easy way to support them prior to the introduction of native threads.
- Directly-Exported Invocation Interface Functions: These functions (GetDefaultJavaVMInitArgs, JNI_CreateJavaVM, and JNI_GetCreatedJavaVMs) are not implemented. This is because we do not provide a virtual machine library that can be linked against, nor do we support native applications that launch and use an embedded Java VM. There is no inherent reason why this could not be done, but we have not done so yet.
Things JNI Can't Handle
- atexit routines: Calling JNI code via a routine run at exit time means calling back into a VM that has been shutdown. This will cause the Jikes RVM to freeze on Intel architectures.
Contributions of any of the missing functionality described here (and associated tests) would be greatly appreciated.