GraalVM Native Image
GraalVM Native Image compile une application Nucleus en amont vers un binaire natif autonome, avec la metadata de réflexion, de ressources et de JNI générée pour chaque module Nucleus.
GraalVM Native Image compile en amont (ahead-of-time) une application Compose Desktop vers un binaire autonome unique qui s'exécute sans JVM. Le compromis, c'est l'hypothèse du monde fermé : pas de chargement dynamique de classes, pas d'agents, et chaque appel par réflexion déclaré au moment du build. Nucleus génère cette metadata pour vous, si bien que la même source qui produit une application JVM produit aussi une image native.
Quand utiliser GraalVM Native Image
- Services en arrière-plan, applications de barre de menu et outils de barre système qui restent inactifs mais doivent rester réactifs.
- Utilitaires et lanceurs où le temps de démarrage compte.
- Distribution sur les stores (App Store, MSIX, Snap) et petits installateurs autonomes qui n'embarquent pas de JRE séparé.
- Environnements à mémoire contrainte, comme les bacs à sable et le matériel bas de gamme.
Compromis
- Pas de JIT. Une image native exécute du code compilé en amont, sans optimisation à la volée : les charges de travail intensives en CPU sur la durée sont donc plus lentes que sur la JVM. Pour ces cas, utilisez plutôt le cache AOT.
- Monde fermé. Pas de
Class.forNamepiloté par des valeurs dynamiques à l'exécution, pas de classloaders personnalisés, pas de moteurs de script, pas de génération de bytecode à l'exécution. Les bibliothèques qui reposent sur ces mécanismes — Spring, Groovy, le mocking basé sur ByteBuddy, un usage intensif de JNA — ne sont pas compatibles ; livrez-les avec le cache AOT. - Builds par plateforme. Une image native doit être compilée sur chaque OS cible : produire des binaires pour macOS, Windows et Linux demande donc une matrice CI.
La plupart des bibliothèques Kotlin idiomatiques — Ktor, kotlinx.serialization, Coil, SQLite, Jewel, Compose, SLF4J — fonctionnent sans configuration supplémentaire.
Pré-requis
Toolchain GraalVM
Nucleus télécharge et met en cache GraalVM Community Edition automatiquement au premier
build natif — vous n'installez plus de toolchain vous-même. Une variable d'environnement
GRAALVM_HOME est prioritaire si vous voulez pointer vers la vôtre. Voir Provisionner la toolchain.
Community Edition par défaut, Oracle GraalVM sur demande
GraalVM CE est le défaut car il est sous licence GPLv2 avec Classpath Exception, qui n'impose
aucune restriction sur la distribution au sein d'une application payante. Basculez sur Oracle
GraalVM avec toolchain { distribution = GraalvmDistribution.ORACLE } si vous avez besoin de
ses optimisations exclusives — Profile-Guided Optimization, -O3, profils inférés par ML et
obfuscation avancée. Oracle GraalVM est gratuit pour un usage en production selon les GraalVM
Free Terms and Conditions, mais les GFTC interdisent de facturer des frais associés à sa
redistribution : relisez-les avant de distribuer une app payante. Le backend AWT, déprécié,
nécessite encore une installation locale de BellSoft Liberica NIK pour le support AWT/Swing en
image native.
Toolchains par plateforme
| Plateforme | Requis |
|---|---|
| macOS | Xcode Command Line Tools |
| Windows | MSVC (Visual Studio Build Tools) |
| Linux | GCC, patchelf et xvfb pour la compilation headless |
Comment Nucleus construit l'image native
Trois modules coopèrent pour produire une image native à partir de la même source qu'un build JVM :
nucleus.graalvm-runtime— la bibliothèque de support runtime. Elle fournit des substitutions SVM@TargetClasspour les parties internes d'AWT qui ne fonctionnent pas telles quelles sous native-image (les gestionnaires de polices Fontconfig et Win32, la création de polices, le splash screen et le WMClass du toolkit X11), enregistre les ressources de l'application et exposeGraalVmInitializer.- Le plugin Gradle Nucleus — génère la reachability metadata (réflexion, ressources, JNI) pour vos dépendances et câble le graphe de tâches
packageGraalvmNative. nucleus.nucleus-application— appelleGraalVmInitializer.initialize()pour vous et expose la même APIDecoratedWindow/NucleusWindowque le chemin JVM, si bien qu'une seule arborescence de sources produit les deux artefacts.
nucleus.application {
mainClass = "com.example.MainKt"
graalvm {
isEnabled = true
imageName = "my-app"
}
}nucleusApplication appelle GraalVmInitializer.initialize() au démarrage. Si vous écrivez votre propre main() sans lui, appelez GraalVmInitializer.initialize() vous-même avant tout code AWT ou Compose.
Et ensuite
- Configuration — le DSL
graalvm { }complet et les arguments de build recommandés. - Metadata automatique — comment la réflexion, les ressources et le JNI sont résolus pour vous.
- Tâches et CI — tâches Gradle, emplacements de sortie et matrice GitHub Actions.
Benchmarks
Résultats de benchmark cross-runtime mesurés — une suite de style Geekbench exécutée dans une vraie application desktop Compose sur JVM JIT, GraalVM Native Image, SwiftUI, Tauri et Flutter.
Configuration
Référence du bloc DSL graalvm { } qui configure les builds native-image GraalVM : toolchain, nom d'image, arguments de build, repository de metadata et réglages par OS.