The HotSpot WhiteBox API
One of the not so well-known tools of the HotSpot VM is its WhiteBox testing API. Introduced in Java 7 it has been significantly improved and extended in Java 8 and 9. It can be used to query or change HotSpot internals which are not otherwise exposed to Java-land. While its features make it an indispensable tool for writing good HotSpot regression tests, it can also be used for experiments or for the mere fun of peeking into the VM. This entry will focus on the usage of the WhiteBox API in Java 8 and 9+.
The WhiteBox API is implemented as a Java class (called sun.hotspot.WhiteBox) which defines various entry points into the HotSpot VM. Most of the functionality is implemented natively, directly in the HotSpot VM. The API is implemented as a singleton which can be easily retrieved by calling the static method WhiteBox.getWhiteBox().
Unfortunately, currently even a simple JavaDoc documentation of the API doesn't exist, so in order to make full use of its functionality, you'll have to peek right into WhiteBox.java.
In JDK 8 the sources were located in the hotspot repository under:
In JDK 9 the Java API was moved to the top-level repository in order to make it accessible for regression tests from other repositories as well. It is now located under:
https://hg.openjdk.org/jdk9/jdk9/file/tip/test/lib/sun/hotspot/WhiteBox.java
In JDK 17 the WhiteBox
class was moved from the sun.hotspot
to the jdk.test.whitebox
package and is located at:
https://github.com/openjdk/jdk17u-dev/blob/master/test/lib/jdk/test/whitebox/WhiteBox.java
Building
In Java 8 the WhiteBox API can be easily built with a special Makefile:
cd hotspot/test/testlibrary/whitebox make BOOTDIR=<path_to_jdk> ls -la wb.jar
This will create wb.jar which contains all the required classes in the current directory.
In Java 9+ the wb.jar file is created as a part of the build-test-lib target:
mkdir output && cd output bash <jdk9_src_root>/configure make build-test-lib ls -la support/test/lib/wb.jar
This will create the library in support/test/lib/wb.jar
. (It is also possible to use the the build-test-lib-only target if the jdk has been built before.)
Usage
Because the WhiteBox API can be used to query and alter internal data structures of the VM its usage is not recommended for productive scenarios. In order to enable its functionality, one hast to place the WhiteBox jar file (i.e. wb.jar
) into the bootstrap class path and one also has to enable the API in the HotSpot VM with the special diagnostic option -XX:+WhiteBoxAPI
. A complete command line may look as follows:
java -Xbootclasspath/a:wb.jar -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI <Program_using_WhiteBox>
Notice that the WhiteBox API is also working with the commercial Oracle JDK, although it is not shipped with it. In general, older versions of wb.jar
will most probably work fine with more recent HotSpot versions while newer versions of the WhiteBox API will usually not work together with an older VM.
WhiteBox JTreg tests
Many HotSpot regression tests already use the WhiteBox API under the hood and the JTreg framework makes it easy to write new ones without having to care about where the API is located and how it can be built. A minimal JTreg test for JDK 21which uses the WhiteBox API looks as follows (taken from https://github.com/openjdk/jdk21u-dev/blob/master/test/lib-test/jdk/test/whitebox/WBApi.java)
/* * @test WBApi * @summary verify that whitebox functions can be linked and executed * @modules java.base/jdk.internal.misc * @library /test/lib * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI WBApi */ import jdk.test.whitebox.WhiteBox; public class WBApi { public static void main(String... args) { System.out.printf("args at: %x\n",WhiteBox.getWhiteBox().getObjectAddress(args)); } }
The test uses the @library
tag to specify the location of the WhiteBox sources (i.e. /test/lib
). In this context an absolute path either refers to the root directory of the JTreg regression tests (e.g. test/jtreghotspot) or to the external.lib.roots property
in the TEST.ROOT
file which is located in the root directory of the test suite (e.g. https://github.com/openjdk/jdk21u-dev/blob/master/test/hotspot/jtreg/TEST.ROOT). By convention, the external.lib.roots
property contains the relative path to the top-level jdk directory (e.g. external.lib.roots
is defined as "../../../"
in test/hotspot/jtreg/TEST.ROOT
). This means that the WhiteBox source used by the HotSpot regression tests can be found under test/hotspot/jtreg/../../../test/lib
which is exactly the location in the OpenJDK top level directory where the sources have been moved to in Java 17.
The "@build jdk.test.whitebox.WhiteBox"
and "@run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox"
JTreg tags instruct JTreg to build and install the wb.jar
file in the directory where the test will be executed. Finally, the second @run
directive executes the regression test in a new VM (because of the main/othervm
attribute) with the required extra options described before (i.e. "-Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
").
For JDKs before 17, the test syntax was slightly different:
/* * @test WBApi * @summary verify that whitebox functions can be linked and executed * @library /testlibrary /test/lib * @build WBApi * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI WBApi */ import sun.hotspot.WhiteBox; public class WBApi { public static void main(String... args) { System.out.printf("args at: %x\n", WhiteBox.getWhiteBox().getObjectAddress(args)); } }
The /testlibrary
path is required to find the sources of the ClassFileInstaller which are located under http://hg.openjdk.java.net/jdk9/jdk9/hotspot/file/tip/test/testlibrary/ClassFileInstaller.java. The "@run main ClassFileInstaller"
directive instructs JTreg to copy the WhiteBox classes to the temporary scratch directory in which the actual test will be executed. This is necessary in order to make it possible to easily add the WhiteBox classes to the boot class path with the help of the -Xbootclasspath/a:.
option.
The second @run
directive finally executes the regression test in a new VM (because of the main/othervm
attribute) with the required extra options described before (i.e. "-Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
").
Implementation
As mentioned before, the WhiteBox class only contains Java wrappers for functionality actually implemented right in the HotSpot virtual machine. The implementation is located in the file http://hg.openjdk.java.net/jdk9/jdk9/hotspot/file/tip/src/share/vm/prims/whitebox.cpp
Notice that the native method WhiteBox.registerNatives()
has no default JNI implementation in the VM but is handled implicitly by the class NativeLookup
(see NativeLookup::lookup_entry()
-> NativeLookup::lookup_style()
-> lookup_special_native()
) and bound to JVM_RegisterWhiteBoxMethods()
which does the actual registration of all the native methods from the WhiteBox class.
In JDK 9 there is just one method (i.e. WhiteBox.deoptimize()
) which is implemented directly as a HotSpot compiler intrinsic.