aotc-gradle-plugin
Plugin for simplifying use of Ahead-of-Time Compilation in Gradle projects.
For those not able (or brave enough) to go fully native with Graal on SubstrateVM.
The plugin facilitates generation of a compiled library for the code which is executed during unit testing. The resulting library is passed (together with the jars) to the JVM on startup. The JVM then loads precompiled classes from the library before falling back to the jars whenever no precompiled class is found.
Ahead-of-time compilation technically corresponds to doing the JVM Hotspot compilation (also known as warmup) during the build process instead of when starting the application. In other words, the application will hit the ground running when starting up.
While it is relatively simple to apply AOT compilation to all your code and dependencies (including the Java base module), this usually produces rather large compiled libraries (in the hundreds of MB range, 322MB for the JDK) which consume unnecessary resources. By using a more targeted approach, this plugin generates smaller compiled libraries.
The plugin requires Java 11 or higher and only works on Linux. Works with all libraries of out the box.
License
Usage
Enable plugin using
plugins {
id "com.github.skjolber.aotc" version "1.0.1"
}
Run the command
./gradlew aotcLibrary --info
for a compiled library at
./build/aotc/aotLibrary.so
Then add the following parameters when starting your application:
-XX:AOTLibrary=./build/aotc/aotLibrary.so
To print the actual classes loaded from the library, also add
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAOT
As aotcLibrary
can take a few minutes to complete, it does not run during normal commands like compile
or build
. Ideally this task only runs on a build server.
Details
Configuration options:
aotc {
garbageCollector = 'g1' // alternatively 'default' or 'parallel'
memory = '1024m' // extra XMX memory for the compilation command
tiered = true // tiered compilation
additionalCommands = files('exclude.txt') // one or more files for additional compile commands
ignoreErrors = true // ignore errors during compilation
captureMode = 'jcmd' // alternatively 'console' or 'mbean'
}
The garbage collector must be the same during compilation and runtime. To guard against misconfiguration, also add
-XX:+UnlockDiagnosticVMOptions -XX:+UseAOTStrictLoading
for a fail fast result.
Note that the new ZGC garbage collector cannot be used with AOT compilation for now.
Capture mode
It turns out that capturing the touched methods via console for now means it is all dumped in the console, which is not very user-friendly if you're looking to scroll up in the console for your test outputs.
Dumping to console does however produce a bit smaller output for the smaller applications, since then there is no JMX and instrumentation agent involved (which touches methods during execution).
For larger applications however, for example for Spring, all capture methods produce approximately the same output, since those additional classes are in use anyhow.
Startup time
Depending on the application, somewhere between 5% and 10% improved startup time is to be expected. Extensive use of reflection will tend to reduce this number.
Using JVM experimental features, is it safe?
AOT compilation was a previously commercially available feature (from Java 9) now available for free in OpenJDK. Experimental features have traditionally been quite stable in the JVM.
The AOT compiler relies on the new Graal JIT compiler, which can also be enabled without AOT compilation (as a regular Hotspot compiler) using
-XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler
Some people are running the new Graal JIT compiler in production (see links). The natural step after AOT compilation is going to GraalVM for a fully native runtime with its Substrate VM. GraalVM is however not yet production ready for Java 12+ and in addition many libraries will need some adjustments to work 100% as well.
Verifying compiled library
For re-running the unit test with the compiled library enabled, for example after first running
./gradlew build aotcLibrary
add
test {
if (project.hasProperty('aotLibrary') && Boolean.parseBoolean(aotLibrary)) {
jvmArgs = ['-XX:AOTLibrary=./build/aotc/aotLibrary.so', '-XX:+UnlockDiagnosticVMOptions', '-XX:+UseAOTStrictLoading']
}
}
to build.gradle
and run
./gradlew cleanTest test -PaotLibrary=true --info
Get involved
If you have any questions, comments or improvement suggestions, please file an issue or submit a pull-request.
Links
Links related to AOT compilation.
- Compile ahead of time. It's fine? by Dmitry Chuyko: Slides
- JEP 295: Ahead-of-Time Compilation
- https://github.com/oracle/graal
- Fast JVM startup with JDK 9
- Twitter’s Quest for a Wholly Graal Runtime
- (JVMLS 2017): Ahead Of Time (AOT) Internals by Vladimir Kozlov and Igor Veresov: Video
- Programmatic jcmd Access
History
- 1.0.1: Maintainance release
- 1.0.0: Initial version / MVP