Loading...

4 min read Original article ↗

Why remove JVMCI from the JDK?

JVMCI is not an ordinary optional Java API that can simply be ignored by users who do not need it. It is an experimental, privileged integration mechanism that lets Java code access HotSpot metadata, participate in compilation, and install machine code together with GC, debug, and deoptimization metadata. In practice, this gives HotSpot two compiler-facing views of VM internals that must be kept consistent.

That consistency has become an expensive ongoing maintenance cost. JVMCI is not isolated in one directory; it affects shared compiler and runtime code, metadata handling, deoptimization, code cache management, GC support, serviceability, flags, tests, and build logic. The removal PR demonstrates this directly: removing JVMCI/Graal references requires changes across many parts of the JDK, and eliminates hundreds of JVMCI-related conditionals, including 252 '#if INCLUDE_JVMCI' blocks. This is not dead weight that sits quietly; it is complexity that many unrelated changes have to carry.

We estimate that around 1.5% of the JDK contributions unexpectedly need to take JVMCI into account. In a one-year sample, after excluding commits that directly targeted JVMCI or Graal, about 75 otherwise unrelated changes still touched JVMCI paths, tests, or conditionals. This suggests JVMCI imposes a recurring collateral cost on ordinary HotSpot and test work.

This cost shows up concretely whenever HotSpot evolves. A change that is conceptually about C2, nmethods, deoptimization, metadata layout, or even GC often also has to ask: does JVMCI have a parallel path, a special metadata representation, a test mode, a flag interaction, or a compatibility requirement? Recent regressions show that this is not hypothetical. The nmethod/CodeCache metadata work is one example:

JDK-8331087

moved immutable nmethod data out of the CodeCache and explicitly included 'jvmci_data'; related nmethod data movement then exposed JVMCI-specific failures such as

JDK-8378195

and

JDK-8355034

. Likewise,

JDK-8364128

was a runtime cleanup for gathering CPU feature names, but it caused

JDK-8365218

, where Graal could see incorrect AArch64 CPU features and emit unsupported instructions. And

JDK-8301995

, which moved invokedynamic resolution information out of ConstantPoolCacheEntry, broke JVMCI’s HotSpotConstantPool lookup path in

JDK-8307588

and also had a related libgraal build breakage. None of these changes was primarily "about JVMCI"; they were ordinary HotSpot evolution. But each still had to be rediscovered and repaired on the JVMCI side. If the JVMCI-specific consequence is noticed early, the original change becomes broader. If it is missed, the breakage surfaces later in a real client such as Graal or Native Image. Either way, mainline JDK development pays the cost, and that cost ultimately slows the progress of Java for everyone.

Project Valhalla provides another concrete example. In deoptimization code, there is now a new path where C2 would provide refined array-property information, while JVMCI currently falls back to default properties. This illustrates the recurring pattern: new VM work cannot simply move forward in one well-understood implementation path; it must either extend JVMCI as well, add conservative fallback behavior, or carry incomplete special handling. That slows down projects like Valhalla and increases the risk of subtle differences between execution modes.

JVMCI also expands the build and test matrix. The JDK has to account for JVMCI enabled and disabled, JVMCI compiler enabled and disabled, different compiler configurations, different platforms, and jtreg properties such as 'vm.jvmci', 'vm.jvmci.enabled' and 'vm.graal.enabled'. This is not just an abstract increase in configuration space: even the tier1 JVMCI tests currently consume around 42 minutes of aggregate machine time. Nor is the cost limited to testing. JVMCI has also required build-system workarounds (for example,

JDK-8197235

). Even when most users do not use JVMCI, mainline still has to preserve, build, and test these paths or accept that they may rot.

This is the central issue: JVMCI imposes broad, recurring costs on JDK mainline, which in turn slows down the progress of Java for all users, for the benefit of a narrow set of downstream use cases. JVMCI was introduced into the JDK to facilitate experiments such as Java-based compilers, with the understanding that if such experiments succeeded, significant architectural work would be required to make JVMCI maintainable in the long run. Given the withdrawal of Projects Galahad and Metropolis, the potential benefits offered by JVMCI are substantially reduced, while the costs remain. The JDK is left carrying an experimental, privileged, poorly encapsulated HotSpot interface that slows unrelated VM evolution, complicates refactoring, increases test burden, and creates additional failure modes for core Java development. This is an experiment that has outlived its usefulness.

Projects that depend on JVMCI should carry and maintain it in their own downstream trees, so that the associated maintenance burden falls on the projects that directly benefit from it. Removing it from the JDK reduces complexity, lowers risk, and lets future HotSpot work proceed with fewer alternate paths to preserve.