Previously I was looking at Java versus Python performance (here) and I was quite surprised (well not surprised, but more intruiged) by the JVM startup speed so I thought i’d have a bit more of a look to satisfy my curiosity. I don’t have all the answers so don’t hassle me, but what I started doing was using the JNI to create a JVM from a simple C command as opposed to using the java command to do the work and invoke the classloader etc. Anyway, this is how you do it in case you’re suitably bored and want to have a go
Here is the ‘c’ code which is a slightly modified version from the JNI reference manual:
#include <jni.h>
#include <stdlib.h>
#define USER_CLASSPATH “.” /* where to find Prog.class */
int main() {
JNIEnv *env;
JavaVM *jvm;
jint res;
jclass cls;
jmethodID mid;
jclass stringClass;
jobjectArray args;
JavaVMInitArgs vm_args;
JavaVMOption options[1];
options[0].optionString =
“-Djava.class.path=” USER_CLASSPATH;
vm_args.version = JNI_VERSION_1_6;
vm_args.options = options;
vm_args.nOptions = 1;
vm_args.ignoreUnrecognized = JNI_TRUE;
/* Create the Java VM */
res = JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res < 0) {
fprintf(stderr, “Can’t create Java VM\n”);
exit(1);
}
exit(0); /* here */
cls = (*env)->FindClass(env, “Prog”);
if (cls == NULL) {
goto destroy;
}
mid = (*env)->GetStaticMethodID(env, cls, “main”,
“([Ljava/lang/String;)V");
if (mid == NULL) {
goto destroy;
}
args = NULL;
(*env)->CallStaticVoidMethod(env, cls, mid, args);
destroy:
if ((*env)->ExceptionOccurred(env)) {
(*env)->ExceptionDescribe(env);
}
(*jvm)->DestroyJavaVM(jvm);
}
Additionally here is the Java code for the class executed by the JVM when instantiated.
public class Prog {
public static void main(String[] args) {
System.out.println(”Hello World”);
}
}
We wont talk about how wordpress has formatted the above - oh well you get the idea
Get things compiled and linked
javac Prog.java
gcc -I /usr/local/jdk1.7.0/include/ -I /usr/local/jdk1.7.0/include/linux/ -L /usr/local/jdk1.7.0/jre/lib/i386 -L /usr/local/jdk1.7.0/jre/lib/i386/client -ljava -ljvm -lverify -o invoke invoke.c
noting that i'm using an openjdk code drop
export LD_LIBRARY_PATH=/usr/local/jdk1.7.0/jre/lib/i386:/usr/local/jdk1.7.0/jre/lib/i386/client
and you should be able to ./invoke to check it out. If you review the invoke.c code above you'll see an exit(0) statement with comment 'here'. If you leave that in and time the execution you will see that the JVM instantiation isn't that bad which is more in line with the empty python example mentioned in the previous blog entry. Removing the exit should see the JVM instantiated and the class file executed.
Interestingly just such a test reveals a far better comparion for VM instantiation with nothing to execute than the previous test. In this case the ratio now becomes :
| Test | Ratio (Java/Python) |
|---|---|
| Interpreter Init | 1.6 |