Performance and native runtimes
Nucleus builds the same Kotlin source into two runtimes — a GraalVM native image or a JDK 25 image with an AOT cache — selected from one Gradle DSL.
Nucleus compiles the same Kotlin source into two runtimes, and you choose which one to produce at build time: a GraalVM native image, or a standard JDK 25 image with an ahead-of-time (AOT) cache. Both are driven by the same Gradle DSL, so switching between them changes the build output, not your code.
The two runtimes
The runtimes differ in how the application is compiled and what it ships with, which leads to different startup and throughput characteristics.
| GraalVM Native Image | JDK 25 + AOT cache | |
|---|---|---|
| Compilation | Ahead-of-time, closed world | JIT at runtime, open world |
| Java runtime | None; self-contained binary | Bundled JRE plus AOT cache |
| Reflection and JNI | Static metadata only | Dynamic, no restrictions |
| Artifact | nucleus.graalvm-runtime | nucleus.aot-runtime |
A GraalVM native image starts quickly and holds a smaller resident set, because it is compiled ahead of time and bundles no JVM. The JDK runtime reaches higher sustained throughput once HotSpot's JIT has warmed up on hot paths; the AOT cache shortens that warmup by reusing class-loading and compilation work from a previous run.
Choose a runtime
- GraalVM Native Image — background utilities, menu-bar apps, CLI-style desktop tools, and sandboxed targets such as the App Store or MSIX. Startup is fast, the installer is small, and no Java runtime ships with the app.
- JDK 25 + AOT cache — long-running, IDE-like tools, data-heavy workloads, plugin hosts, and anything that relies on dynamic behavior such as scripting engines, ByteBuddy, or custom class loaders. HotSpot's JIT pays off once hot paths warm up, and the AOT cache removes most of the warmup cost.
- Both — the two can coexist in one project. A team might ship a GraalVM build to the App Store and a JDK build with an AOT cache for direct download.
Switch between runtimes
Both runtimes are configured in the nucleus.application block. Enabling graalvm produces a
native image; setting enableAotCache produces a JDK image with an AOT cache.
nucleus.application {
mainClass = "com.example.MainKt"
// Produce a GraalVM native image.
graalvm {
isEnabled = true
imageName = "myapp"
}
// Or produce a JDK 25 image with an AOT cache.
nativeDistributions {
enableAotCache = true
}
}Both blocks can be present at once. Select which runtime a build produces per build type or per CI matrix entry, or produce both and let users choose.
Supporting modules
The native builds rely on a few modules that supply platform metadata and OS integration:
- Native access — how Nucleus supplies GraalVM's reflection, resource, and JNI metadata so you don't hand-write JSON.
- Native code in Kotlin — write the native side in Kotlin/Native and call it from the JVM through a generated FFM bridge.
- Native HTTP —
HttpClient, OkHttp, and Ktor wired to the OS trust store. - Native SSL — the OS keychain as the trust-anchor source.
What's next
- GraalVM Native Image — the full Gradle DSL, automatic metadata, Gradle tasks, and CI wiring.
- AOT cache — JDK 25's Project Leyden, connected to Gradle in one line.
- Runtimes — the architecture behind the two backends.
Filesystem watcher
Watch files and directories for changes from Kotlin and receive create, modify, remove, and move events as a coroutine Flow.
GraalVM Native Image
GraalVM Native Image compiles a Nucleus app ahead of time into a self-contained native binary, with reflection, resource, and JNI metadata generated for every Nucleus module.