Java In-Memory ClassReader
Posted: October 5, 2015
A while ago I was working on a project that required using JNI to set a bunch of fields in a class. To set fields using JNI, functions such as SetIntField() have to be called, which seems like it could be quite a performance bottleneck. I typically estimate the smallest function call to be around 100 cpu cycles. While working on Java Grinder for x86, I was benchmarking my generated assembly code against gcc compiled C and Java's built in JIT and found Java's built in JIT was neck and neck with gcc's code. So then this idea hit me. Would it be possible to create a Java class file in memory with a generated method that would set fields in an instance of an object. It turns out it is possible.
On a side note, I think it might actually be possible to create a full Java class in memory with byte-code using Java itself, but this library would be useful to me for other future projects.
Related Projects @mikekohn.net
Before I got started on this, I wanted to make sure that Java was going to be faster than JNI so I create a Java class with 20 fields in it. In my test program I allocated an array of args.length * 4 bytes and filled in every 4 bytes with Integer.parseInt(args[index]) in little endian format. I then wrote a JNI function which takes that byte array, casts each 4 bytes to an int, and places them in fields0 to fields19 in reverse order. I then wrote a Java method which takes every 4 bytes and shift / or's them into an integer and sets fields0 to fields 19 again in reverse order. The JNI code is in the github repo in a file called myobject.c and the Java code and field definitions are in a file called MyObject.java.
Oddly, the JNI code was running around 2300 cycles while the Java code was running more like 6000 cycles. Before giving up I found out that Java typically won't run through a heavily optimized JIT (or maybe no JIT at all) unless a method gets called many times over. I found a way to force the Java to always heavily compile all methods by passing in a -Xcomp argument to the Java runtime. Now my numbers were looking more like 5000 cycles for JNI and 900 cycles for Java. Still a bit confusing since the JNI part got slower. Either way, I decided this was worth continuing.
My next step is to write code to both be able to read a class in and dump the contents for debugging (similar to what javap does) and write code to be able to create a class in memory giving the option to write it to disk or load it directly into the JVM. I was originally going to write the whole thing pure C, but then decided the the class creation module would look nicer in C++. In the end I decided the class reader looked nicer in C and converted just that part back to straight C.
After getting the class writer working, I created a JNI layer on top of it. From there Test.java (from the github repository) could create a class in memory and call methods in it.
Since the class writing part of the project is contained in a single ClassWriter.cxx module, I now have ideas for other projects that could actually use this. Maybe a simple scripting language that outputs Java .class files or such?
Copyright 1997-2021 - Michael Kohn