The biggest difference between the GNU Classpath and Sun OpenJDK VM interfaces is the point at which control shifts from the library to the virtual machine. Both solutions do provide separation between the library and the VM. Contrary to what may be initially assumed, this is true of OpenJDK even though both HotSpot and the JDK are maintained in the same location. This is what allows different versions of HotSpot to be swapped in, as mentioned in the OpenJDK trademark license. That said, there are likely to be closer ties between the JDK within OpenJDK and HotSpot than there are between GNU Classpath and any of its VMs, simply because of the number of and variance between the latter.

OpenJDK’s VM interface is entirely C-based. The class library calls into the VM using a number of functions with the prefix ‘JVM_’ that are listed in src/share/javavm/export/jvm.h. Implementations of these functions can be found both in HotSpot’s src/share/vm/prims/jvm.cpp and CACAO’s src/native/vm/openjdk/jvm.c. These are dynamically linked in at runtime for a variety of dynamic libraries held in jre/lib/${arch} where ${arch} is the architecture in use such as amd64. The output below shows their use in the recent b31 drop:

Checking build/linux-amd64/j2sdk-image/jre/lib/amd64/libjava.so...
                 U JVM_ActiveProcessorCount@@SUNWprivate_1.1
                 U JVM_ArrayCopy@@SUNWprivate_1.1
                 U JVM_AssertionStatusDirectives@@SUNWprivate_1.1
                 U JVM_Available@@SUNWprivate_1.1
                 U JVM_ClassDepth@@SUNWprivate_1.1
                 U JVM_ClassLoaderDepth@@SUNWprivate_1.1
                 U JVM_Clone@@SUNWprivate_1.1
                 U JVM_Close@@SUNWprivate_1.1
                 U JVM_CompileClass@@SUNWprivate_1.1
                 U JVM_CompileClasses@@SUNWprivate_1.1
                 U JVM_CompilerCommand@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetClassAt@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetClassAtIfLoaded@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetDoubleAt@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetFieldAt@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetFieldAtIfLoaded@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetFloatAt@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetIntAt@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetLongAt@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetMemberRefInfoAt@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetMethodAt@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetMethodAtIfLoaded@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetSize@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetStringAt@@SUNWprivate_1.1
                 U JVM_ConstantPoolGetUTF8At@@SUNWprivate_1.1
                 U JVM_CountStackFrames@@SUNWprivate_1.1
                 U JVM_CurrentClassLoader@@SUNWprivate_1.1
                 U JVM_CurrentLoadedClass@@SUNWprivate_1.1
                 U JVM_CurrentThread@@SUNWprivate_1.1
                 U JVM_CurrentTimeMillis@@SUNWprivate_1.1
                 U JVM_DefineClassWithSource@@SUNWprivate_1.1
                 U JVM_DesiredAssertionStatus@@SUNWprivate_1.1
                 U JVM_DisableCompiler@@SUNWprivate_1.1
                 U JVM_DoPrivileged@@SUNWprivate_1.1
                 U JVM_DumpThreads@@SUNWprivate_1.1
                 U JVM_EnableCompiler@@SUNWprivate_1.1
                 U JVM_FillInStackTrace@@SUNWprivate_1.1
                 U JVM_FindClassFromClassLoader@@SUNWprivate_1.1
                 U JVM_FindLibraryEntry@@SUNWprivate_1.1
                 U JVM_FindLoadedClass@@SUNWprivate_1.1
                 U JVM_FindPrimitiveClass@@SUNWprivate_1.1
                 U JVM_FindSignal@@SUNWprivate_1.1
                 U JVM_FreeMemory@@SUNWprivate_1.1
                 U JVM_GC@@SUNWprivate_1.1
                 U JVM_GetAllThreads@@SUNWprivate_1.1
                 U JVM_GetArrayElement@@SUNWprivate_1.1
                 U JVM_GetArrayLength@@SUNWprivate_1.1
                 U JVM_GetCallerClass@@SUNWprivate_1.1
                 U JVM_GetClassAccessFlags@@SUNWprivate_1.1
                 U JVM_GetClassAnnotations@@SUNWprivate_1.1
                 U JVM_GetClassConstantPool@@SUNWprivate_1.1
                 U JVM_GetClassContext@@SUNWprivate_1.1
                 U JVM_GetClassDeclaredConstructors@@SUNWprivate_1.1
                 U JVM_GetClassDeclaredFields@@SUNWprivate_1.1
                 U JVM_GetClassDeclaredMethods@@SUNWprivate_1.1
                 U JVM_GetClassInterfaces@@SUNWprivate_1.1
                 U JVM_GetClassLoader@@SUNWprivate_1.1
                 U JVM_GetClassModifiers@@SUNWprivate_1.1
                 U JVM_GetClassName@@SUNWprivate_1.1
                 U JVM_GetClassSignature@@SUNWprivate_1.1
                 U JVM_GetClassSigners@@SUNWprivate_1.1
                 U JVM_GetComponentType@@SUNWprivate_1.1
                 U JVM_GetDeclaredClasses@@SUNWprivate_1.1
                 U JVM_GetDeclaringClass@@SUNWprivate_1.1
                 U JVM_GetEnclosingMethodInfo@@SUNWprivate_1.1
                 U JVM_GetInheritedAccessControlContext@@SUNWprivate_1.1
                 U JVM_GetInterfaceVersion@@SUNWprivate_1.1
                 U JVM_GetLastErrorString@@SUNWprivate_1.1
                 U JVM_GetPrimitiveArrayElement@@SUNWprivate_1.1
                 U JVM_GetProtectionDomain@@SUNWprivate_1.1
                 U JVM_GetStackAccessControlContext@@SUNWprivate_1.1
                 U JVM_GetStackTraceDepth@@SUNWprivate_1.1
                 U JVM_GetStackTraceElement@@SUNWprivate_1.1
                 U JVM_GetSystemPackage@@SUNWprivate_1.1
                 U JVM_GetSystemPackages@@SUNWprivate_1.1
                 U JVM_Halt@@SUNWprivate_1.1
                 U JVM_HoldsLock@@SUNWprivate_1.1
                 U JVM_IHashCode@@SUNWprivate_1.1
                 U JVM_InitProperties@@SUNWprivate_1.1
                 U JVM_InternString@@SUNWprivate_1.1
                 U JVM_Interrupt@@SUNWprivate_1.1
                 U JVM_InvokeMethod@@SUNWprivate_1.1
                 U JVM_IsArrayClass@@SUNWprivate_1.1
                 U JVM_IsInterface@@SUNWprivate_1.1
                 U JVM_IsInterrupted@@SUNWprivate_1.1
                 U JVM_IsNaN@@SUNWprivate_1.1
                 U JVM_IsPrimitiveClass@@SUNWprivate_1.1
                 U JVM_IsSupportedJNIVersion@@SUNWprivate_1.1
                 U JVM_IsThreadAlive@@SUNWprivate_1.1
                 U JVM_LatestUserDefinedLoader@@SUNWprivate_1.1
                 U JVM_LoadLibrary@@SUNWprivate_1.1
                 U JVM_Lseek@@SUNWprivate_1.1
                 U JVM_MaxMemory@@SUNWprivate_1.1
                 U JVM_MaxObjectInspectionAge@@SUNWprivate_1.1
                 U JVM_MonitorNotify@@SUNWprivate_1.1
                 U JVM_MonitorNotifyAll@@SUNWprivate_1.1
                 U JVM_MonitorWait@@SUNWprivate_1.1
                 U JVM_NanoTime@@SUNWprivate_1.1
                 U JVM_NativePath@@SUNWprivate_1.1
                 U JVM_NewArray@@SUNWprivate_1.1
                 U JVM_NewInstanceFromConstructor@@SUNWprivate_1.1
                 U JVM_NewMultiArray@@SUNWprivate_1.1
                 U JVM_Open@@SUNWprivate_1.1
                 U JVM_RaiseSignal@@SUNWprivate_1.1
                 U JVM_Read@@SUNWprivate_1.1
                 U JVM_RegisterSignal@@SUNWprivate_1.1
                 U JVM_ResolveClass@@SUNWprivate_1.1
                 U JVM_ResumeThread@@SUNWprivate_1.1
                 U JVM_SetArrayElement@@SUNWprivate_1.1
                 U JVM_SetClassSigners@@SUNWprivate_1.1
                 U JVM_SetLength@@SUNWprivate_1.1
                 U JVM_SetPrimitiveArrayElement@@SUNWprivate_1.1
                 U JVM_SetProtectionDomain@@SUNWprivate_1.1
                 U JVM_SetThreadPriority@@SUNWprivate_1.1
                 U JVM_Sleep@@SUNWprivate_1.1
                 U JVM_StartThread@@SUNWprivate_1.1
                 U JVM_StopThread@@SUNWprivate_1.1
                 U JVM_SupportsCX8@@SUNWprivate_1.1
                 U JVM_SuspendThread@@SUNWprivate_1.1
                 U JVM_Sync@@SUNWprivate_1.1
                 U JVM_TotalMemory@@SUNWprivate_1.1
                 U JVM_TraceInstructions@@SUNWprivate_1.1
                 U JVM_TraceMethodCalls@@SUNWprivate_1.1
                 U JVM_UnloadLibrary@@SUNWprivate_1.1
                 U JVM_Write@@SUNWprivate_1.1
                 U JVM_Yield@@SUNWprivate_1.1
Checking build/linux-amd64/j2sdk-image/jre/lib/amd64/libmanagement.so...
                 U JVM_ActiveProcessorCount@@SUNWprivate_1.1
                 U JVM_GetAllThreads@@SUNWprivate_1.1
                 U JVM_GetManagement@@SUNWprivate_1.1
Checking build/linux-amd64/j2sdk-image/jre/lib/amd64/libnet.so...
                 U JVM_Connect@@SUNWprivate_1.1
                 U JVM_CurrentTimeMillis@@SUNWprivate_1.1
                 U JVM_FindLibraryEntry@@SUNWprivate_1.1
                 U JVM_GetHostName@@SUNWprivate_1.1
                 U JVM_GetSockName@@SUNWprivate_1.1
                 U JVM_GetSockOpt@@SUNWprivate_1.1
                 U JVM_InitializeSocketLibrary@@SUNWprivate_1.1
                 U JVM_Listen@@SUNWprivate_1.1
                 U JVM_Send@@SUNWprivate_1.1
                 U JVM_SetSockOpt@@SUNWprivate_1.1
                 U JVM_Socket@@SUNWprivate_1.1
                 U JVM_SocketAvailable@@SUNWprivate_1.1
                 U JVM_SocketClose@@SUNWprivate_1.1
                 U JVM_SocketShutdown@@SUNWprivate_1.1
Checking build/linux-amd64/j2sdk-image/jre/lib/amd64/librmi.so...
                 U JVM_LatestUserDefinedLoader@@SUNWprivate_1.1
Checking build/linux-amd64/j2sdk-image/jre/lib/amd64/libverify.so...
                 U JVM_FindClassFromClass@@SUNWprivate_1.1
                 U JVM_GetCPClassNameUTF@@SUNWprivate_1.1
                 U JVM_GetCPFieldClassNameUTF@@SUNWprivate_1.1
                 U JVM_GetCPFieldModifiers@@SUNWprivate_1.1
                 U JVM_GetCPFieldSignatureUTF@@SUNWprivate_1.1
                 U JVM_GetCPMethodClassNameUTF@@SUNWprivate_1.1
                 U JVM_GetCPMethodModifiers@@SUNWprivate_1.1
                 U JVM_GetCPMethodNameUTF@@SUNWprivate_1.1
                 U JVM_GetCPMethodSignatureUTF@@SUNWprivate_1.1
                 U JVM_GetClassCPEntriesCount@@SUNWprivate_1.1
                 U JVM_GetClassCPTypes@@SUNWprivate_1.1
                 U JVM_GetClassFieldsCount@@SUNWprivate_1.1
                 U JVM_GetClassMethodsCount@@SUNWprivate_1.1
                 U JVM_GetClassNameUTF@@SUNWprivate_1.1
                 U JVM_GetFieldIxModifiers@@SUNWprivate_1.1
                 U JVM_GetMethodIxArgsSize@@SUNWprivate_1.1
                 U JVM_GetMethodIxByteCode@@SUNWprivate_1.1
                 U JVM_GetMethodIxByteCodeLength@@SUNWprivate_1.1
                 U JVM_GetMethodIxExceptionIndexes@@SUNWprivate_1.1
                 U JVM_GetMethodIxExceptionTableEntry@@SUNWprivate_1.1
                 U JVM_GetMethodIxExceptionTableLength@@SUNWprivate_1.1
                 U JVM_GetMethodIxExceptionsCount@@SUNWprivate_1.1
                 U JVM_GetMethodIxLocalsCount@@SUNWprivate_1.1
                 U JVM_GetMethodIxMaxStack@@SUNWprivate_1.1
                 U JVM_GetMethodIxModifiers@@SUNWprivate_1.1
                 U JVM_GetMethodIxNameUTF@@SUNWprivate_1.1
                 U JVM_GetMethodIxSignatureUTF@@SUNWprivate_1.1
                 U JVM_IsConstructorIx@@SUNWprivate_1.1
                 U JVM_IsInterface@@SUNWprivate_1.1
                 U JVM_IsSameClassPackage@@SUNWprivate_1.1
                 U JVM_ReleaseUTF@@SUNWprivate_1.1
Checking build/linux-amd64/j2sdk-image/jre/lib/amd64/libzip.so...
                 U JVM_Close@@SUNWprivate_1.1
                 U JVM_GetLastErrorString@@SUNWprivate_1.1
                 U JVM_Lseek@@SUNWprivate_1.1
                 U JVM_NativePath@@SUNWprivate_1.1
                 U JVM_Open@@SUNWprivate_1.1
                 U JVM_RawMonitorCreate@@SUNWprivate_1.1
                 U JVM_RawMonitorDestroy@@SUNWprivate_1.1
                 U JVM_RawMonitorEnter@@SUNWprivate_1.1
                 U JVM_RawMonitorExit@@SUNWprivate_1.1

Clearly most of these are found in libjava.so, which is the library that contains the native code for the core classes like those in java.lang. What’s interesting about libjava is that, unlike for example libnet, it isn’t loaded via a call to LoadLibrary in the class library code. Rather, it is expected that the VM will know about and load this. In CACAO, a special OpenJDK case is provided in src/native/vm/nativevm.c to handle this. This needs to happen early in the VM initialisation process before any of the native calls in the core classes are used.

In contrast, GNU Classpath interacts with the VM while still at the Java level. Each call which may need to go to the VM is first handed off to a package-private VM class, for example vm/reference/java/lang/VMObject.java provides methods like wait() and notify(). The reference version of these classes tend to do what the OpenJDK classes do in the original classes like java.lang.Object; define the methods as native. However, a VM can replace these VM* classes as needed. Some are mandatory, as it isn’t possible to provide a generic reference implementation in the class library. In both cases, missing implementations are visible through linking errors at runtime.

To further clarify the difference, let’s trace the path of one such function, java.lang.Object#wait(). In GNU Classpath, java.lang.Object defines it as:

  public final void wait()
    throws IllegalMonitorStateException, InterruptedException
  {
    VMObject.wait(this, 0, 0);
  }

in java/lang/Object.java. In the reference version of VMObject.java, we find:

  static native void wait(Object o, long ms, int ns)
    throws IllegalMonitorStateException, InterruptedException;

In some cases, there is also a reference native implementation under native/jni/${package name}/${package_name}_${class_name}.c (native/jni/java-lang/java_lang_VMObject.c in this case), but this isn’t the case here. Instead, we look to CACAO and find this in src/native/vm/gnuclasspath/java_lang_VMObject.cpp:

JNIEXPORT void JNICALL Java_java_lang_VMObject_wait(JNIEnv *env, jclass clazz, java_lang_Object *o, int64_t ms, int32_t ns)

How about for OpenJDK? Again, we start in java.lang.Object and find this in src/share/classes/java/lang/Object.java:

public final native void wait(long timeout) throws InterruptedException;

Unlike with GNU Classpath, the java.lang.Object code goes straight to the native implementation found in src/share/native/java/lang/Object.c. However, we don’t find an implementation there. Instead, we need to look at another function in Object.c called registerNatives:

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
    (*env)->RegisterNatives(env, cls,
                            methods, sizeof(methods)/sizeof(methods[0]));
}

The array, methods, tells us that wait maps to JVM_MonitorWait. Looking at the list above, we see that JVM_MonitorWait is one of the VM symbols in the java library. And sure enough, we find it in src/share/javavm/export/jvm.h:

JNIEXPORT void JNICALL
JVM_MonitorWait(JNIEnv *env, jobject obj, jlong ms);

and a corresponding implementation in HotSpot’s src/share/vm/prims/jvm.cpp

JVM_ENTRY(void, JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms))

and CACAO’s jvm.c:

void JVM_MonitorWait(JNIEnv* env, jobject handle, jlong ms)

Clearly, there isn’t that much difference between the two. The main issue is finding the right functions and where and how they are called. One other major difference is that Classpath VMs provide their own launchers (you run ‘cacao’, ‘jamvm’, ‘gij’, ‘kaffe’ etc. binaries) while the ‘java’ in OpenJDK is a standard launcher (also used for other tools) which invokes the VM via JNI and libjvm.so. There’s also a mechanism for selecting VM in this manner through src/(solaris|windows)/bin/${arch}/jvm.cfg, which we’ll look at later.

With the CVMI project, we aim to document these issues and also experiment with providing an OpenJDK interface more at the Java level. Comments and suggestions welcome.