A "virtual" virtual machine for Java bytecode: JVVM

Sometimes, it is useful to have control over the execution of a Java virtual machine at single bytecode step granularity. We required this for measuring the cost of insertion operations for java.util.Collection implementations. For a single insertion operation, the timers supplied by the OS are far too coarse, and since an insert operation will change the cost of further operations, adding an element a million times is not suitable for determining the cost of a single insertion operation. So we decided to count bytecode execution steps as an approximation for runtime cost.

smallest elementrandom element
Number of bytecode steps required for searching an element in a java.util.TreeSet, depending on the number of elements added before (round dots are the result of a curve fitting)

A virtual machine needs to do a number of tasks:

For the first task, we utilize the magnificent BCEL library. The second task we want to implement ourselves (but heavily depend on the provisions of BCEL). For the last task, we just use Java (hence the "virtual" virtual machine designation). If we interpret a NEW bytecode instruction, we just generate an appropriate object ourselves (using the Java reflection API). Java takes care of the memory allocation and garbage collection, and we can continue to interpret the constructor, which will invariably be the next thing the bytecode does. Using such a strategy also allows for easy inclusion of native calls, since all the Java objects are right there in the memory, and native calls can operate on them without any problems. Hence, our virtual virtual machine is not very complex, although it is admittedly very much slower than the standard Java VM.

There is one problem that needs to be solved for the strategy of reusing the Java VM memory management: When creating a new object, Java requires that we call a constructor. Although we will simulate the constructor call of the bytecode later, we need to call a constructor right when instantiating the object. This is problematic: even if there is a parameter-less constructor available, it might have side-effectts (e.g., setting static variables). Our solution is to do a little bytecode engineering and modify the class we generate the object from by adding a constructor that does nothing at all. Obviously, we have to apply such a change to the superclasses as well, so we use the JVMTI API to add a class loading hook and supply each class with a constructor that takes a not.defined.Anywhere parameter, and will hence not interfere with existing constructors.

Other than this "workaround", the JVVM is basically a straight-forward implementation of the BCEL bytecode visitor.

Download and Usage Note

The sources can be downloaded here: jvvm.tar.gz. The source-code is released under a BSD-style license.

The project is utterly undocumented and I do not want to make a quick attempt at providing some incomplete documentation now. If someone is interested in this kind of project, I will gladly provide documentation and help.


Moritz Hammer, 20.2.2009