GraalVM Native Image
Kurzantwort: CDMS wird produktiv als natives Binary betrieben – kürzere Startzeit und deutlich weniger RAM (Erfahrungswert: rund 400 MB auf unter 200 MB). Der Preis ist, dass jede Reflexion vorab bekannt sein muss. Die festen Modultypen registrieren die Module selbst, die anwendungsspezifischen Typen registriert der Generator.
Aufteilung der Registrierung
Abschnitt betitelt „Aufteilung der Registrierung“| Gruppe | wer registriert | wo |
|---|---|---|
Feste Modultypen: Basisklassen (AbstractEntityModel, AbstractSystemModel, AbstractTenantModel, AbstractUserModel), Envers-Typen | Persistenzmodul | aot.CdmsPersistenceRuntimeHints, eingebunden über @ImportRuntimeHints in aot.CdmsPersistenceAotConfiguration |
Konkrete Entities aus der EntityRegistry | Generator | JpaNativeHints |
| DTOs und Payloads (Jackson) | Generator | JacksonNativeHints |
Enums, die als Filterwerte über valueOf(String) laufen | Generator | JpaNativeHints |
| Weitere reflexiv genutzte Typen der Anwendung | Anwendung | eigene RuntimeHints-Klasse |
| Aktivierung im Projekt | Scaffold | config/NativeHintsConfiguration |
Die Basisklassen brauchen die Registrierung, weil ihre geerbten Accessoren
(get_createdOn, getId, get_userId, …) bei jeder Entity reflexiv
aufgerufen werden.
Wo überall Reflexion im Spiel ist
Abschnitt betitelt „Wo überall Reflexion im Spiel ist“| Stelle | Art |
|---|---|
| System-Layer | MethodHandles.lookup().findVirtual(...) für jeden Getter/Setter |
| Persistenz | gecachte Getter/Setter (GETTER_CACHE, SETTER_CACHE) inklusive der Unterstrich-Systemfelder |
| Jackson | Deserialisierung von DTOs und Payloads |
| Hibernate | Entity-Verarbeitung, Envers |
| Hooks | Instanziierung von Feld-Hooks über getConstructor().newInstance() |
Das ist der Grund, warum Native Image bei CDMS kein Nebenthema ist: Der Kern arbeitet durchgehend reflexiv über generierte Modelle.
# benötigt GraalVM Community 24./mvnw clean package -Pnative -DskipTests./mvnw -Pnative native:compile -DskipTestsIn der CI läuft der Build im Image native-image-community:24; das Ergebnis
target/cdms wandert in ein debian:bookworm-slim-Image.
Typische Fehlerbilder
Abschnitt betitelt „Typische Fehlerbilder“| Symptom | Ursache / Abhilfe |
|---|---|
NoClassDefFoundError oder Reflexionsfehler nur im nativen Image | fehlende Hints – @RegisterReflectionForBinding bzw. RuntimeHints ergänzen |
| Jackson kann ein Payload nicht deserialisieren | Klasse fehlt in JacksonNativeHints |
| Enum-Filter schlägt fehl | valueOf(String) nicht registriert |
| Build verlangt GraalVM | JDK auf GraalVM Community 24 stellen |
Was zu beachten ist
Abschnitt betitelt „Was zu beachten ist“- Neue reflexiv genutzte Typen brauchen Hints. Wer eine eigene Klasse per Reflexion oder Jackson anspricht, registriert sie – am besten dort, wo sie entsteht.
- Der Generator ist die richtige Stelle für Modelltypen. Manuell gepflegte Listen laufen mit dem nächsten Modell auseinander.
- Der Beweis ist der Build. Nur ein GraalVM-Build in der CI zeigt, ob das Image wirklich läuft.
Warum sich das lohnt
Abschnitt betitelt „Warum sich das lohnt“| Aspekt | JVM | Native |
|---|---|---|
| Startzeit | Sekunden | Bruchteile davon |
| RAM nach dem Start | ~400 MB (gemessener Fall) | < 200 MB |
| Build-Dauer | kurz | deutlich länger |
| Reflexion | frei | nur mit Hints |
In Kubernetes mit vielen kleinen Diensten ist der Speicherunterschied der ausschlaggebende Punkt.