Dual JVM Mode
September 24, 2025 ยท View on GitHub
Dual JVM mode allows Enso to run both native-compiled code and JVM bytecode in the same process. This enables fast startup and efficient execution of libraries that require the JVM.
Terminology
- Native Image (NI): A compiled native executable.
- Substrate VM (SVM): Java runtime for NI
- "host JVM" in the production mode
- HotSpot VM: The standard Java Virtual Machine.
- "guest JVM" in the production mode
- the only JVM when
--jvmswitch is used
- Channel connects the two JVMs
together
- can be mocked in a single JVM
How It Works
- The core of Enso is compiled to a native executable (NI).
- Some standard, essential libraries are compiled into NI as well
- Other less essential libraries are only compiled to JVM bytecode.
- NI cannot run JVM bytecode directly.
- SVM is not JVM bytecode interpreter.
- When JVM bytecode is needed, SVM loads the HotSpot VM as a shared library.
- Both VMs run in the same process and communicate efficiently.
Motivation
- Native compilation can make executables very large and slow to build.
- Maintaining NI configuration is complex, especially for third-party libraries.
- Fast startup is important, so NI is the entry point.
- Some components will only be compiled to the JVM bytecode.
- Executing JVM bytecode in a separate process is not efficient.
Forcing JVM Mode
- When the
--jvmoption is passed to the engine runner, NI immediately delegates execution to the HotSpot VM.- This is called JVM mode.
- When a library that is not NI-ready (contains JAR files) is loaded.
- A library can require JVM mode by setting
jvm: truein its descriptor.
Debugging a Mock Mode
- to mock "dual JVM" mode in HotSpot JVM specify which libraries should use the "host JVM" (e.g. behave like being compiled into NI in production) and which the "guest JVM" (e.g. be loaded via HotSpot in production).
- For example:
sbt:enso> runEngineDistribution --jvm
--vm.D=polyglot.enso.classLoading=Standard.Base:hosted,guest
--run test/Base_Tests/ --debug
- says that
Standard.Baseshould use host JVM and all other libraries should use the guest JVM - this mode is then easy to debug as everything is mocked inside of a single HotSpot JVM
Observing the Channel Communication
- Since #13780 enable histogram
of messages with
org.enso.jvm.interop.limitproperty - For example by:
sbt:enso> runEngineDistribution --jvm
--vm.D=org.enso.jvm.interop.limit=100000
--vm.D=polyglot.enso.classLoading=Standard.Base:hosted,guest
--run test/Generic_JDBC_Tests
- one instructs the system to process messages and after each 100000 messages
- print a histogram with most frequently used message types
References
Related Work
- Emit a warning on non-AOT ready libraries
- Handle --jvm flag in process with JNI instantiated JVM
- Callback to native executable from HotSpot JVM
- Channel connecting two JVM instances
- Using Channel to load Java classes
- Moving polyglot java import handling into EpbLanguage
- Use Dual JVM mode to run StdLib Native tests
- Benchmark and speed dual JVM mode up
- Distributed GC between the dual JVMs
- No need for jvm:true when running Generic_JDBC_Tests
- Exchanging direct ByteBuffers between the dual JVMs