{"id":649,"date":"2026-03-14T21:47:57","date_gmt":"2026-03-14T20:47:57","guid":{"rendered":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=649"},"modified":"2026-06-16T21:49:39","modified_gmt":"2026-06-16T20:49:39","slug":"graalvm-native-images-java-anwendungen-als-native-executables","status":"publish","type":"post","link":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=649","title":{"rendered":"GraalVM Native Images \u2013 Java-Anwendungen als native Executables"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Java ist bekannt f\u00fcr seine Plattformunabh\u00e4ngigkeit: Einmal kompilierter Bytecode l\u00e4uft auf jeder JVM. Doch dieser Komfort hat seinen Preis \u2013 die JVM muss zur Laufzeit starten, Klassen laden, Bytecode interpretieren und zur Laufzeit optimieren (JIT). F\u00fcr Microservices, Serverless Functions oder CLI-Tools kann dieser Overhead st\u00f6rend sein, besonders in Container-Umgebungen, wo kurze Startzeiten und geringer Speicherverbrauch z\u00e4hlen. Hier kommen GraalVM Native Images ins Spiel.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Was ist GraalVM?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GraalVM ist eine High-Performance-Laufzeitumgebung, die \u00fcber eine klassische JVM hinausgeht. Kernst\u00fcck ist der Ahead-of-Time (AOT) Compiler, der Java-Bytecode direkt in nativen Maschinencode \u00fcbersetzt \u2013 noch bevor das Programm ausgef\u00fchrt wird. Das Ergebnis ist eine standalone, ausf\u00fchrbare Binary, die komplett ohne JVM auskommt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">GraalVM wurde urspr\u00fcnglich von Oracle Labs entwickelt und ist mittlerweile in zwei Editionen verf\u00fcgbar: die Community Edition (CE) als Open Source und die Enterprise Edition (EE) mit zus\u00e4tzlichen Optimierungen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Vorteile von Native Images<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Schneller Start<\/strong>: Ein Spring Boot REST-Service startet in Millisekunden statt 3\u20135 Sekunden auf der JVM<\/li>\n\n\n\n<li><strong>Geringer Speicherverbrauch<\/strong>: Native Images ben\u00f6tigen oft nur 10\u201330 MB RAM, w\u00e4hrend die \u00e4quivalente JVM-Anwendung 100\u2013300 MB belegt<\/li>\n\n\n\n<li><strong>Keine JVM-Abh\u00e4ngigkeit<\/strong>: Die Binary l\u00e4sst sich ohne installiertes JDK ausf\u00fchren \u2013 ideal f\u00fcr minimale Docker-Images (~10 MB statt 200+ MB)<\/li>\n\n\n\n<li><strong>Sofortige Spitzenleistung<\/strong>: Keine JIT-Compilation-Warm-Up-Phase; das native Image l\u00e4uft ab Start auf seinem maximalen Performance-Niveau \u2013 zu beachten ist, dass JIT-optimierter JVM-Code bei l\u00e4nger laufenden Prozessen durch Profiling-basierte Optimierungen h\u00f6here Durchsatzraten erreichen kann<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Nachteile und Einschr\u00e4nkungen<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>L\u00e4ngere Build-Zeit<\/strong>: Der AOT-Compiler muss das gesamte Programm inklusive aller Abh\u00e4ngigkeiten analysieren (oft 1\u20135 Minuten)<\/li>\n\n\n\n<li><strong>Closed-World-Annahme<\/strong>: Reflection, dynamische Klassenladung (<code>Class.forName()<\/code>), Serialisierung und dynamische Proxys sind nur eingeschr\u00e4nkt nutzbar<\/li>\n\n\n\n<li><strong>Kein dynamisches Classloading<\/strong>: Frameworks, die CGLIB oder CDI-Proxys nutzen, ben\u00f6tigen explizite Konfiguration<\/li>\n\n\n\n<li><strong>Eingeschr\u00e4nkte Debugging-M\u00f6glichkeiten<\/strong>: Stacktraces sind weniger aussagekr\u00e4ftig als auf der JVM<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Erste Schritte mit Native Images<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Zuerst muss GraalVM installiert sein. Mit SDKMan geht das am einfachsten:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">sdk<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-selector-tag\">java<\/span> 24<span class=\"hljs-selector-class\">.0<\/span><span class=\"hljs-selector-class\">.1-graal<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Danach installiert man das&nbsp;<code>native-image<\/code>&nbsp;Tool:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">gu install native-image\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Eine einfache Java-Klasse l\u00e4sst sich wie folgt nativ kompilieren:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">public <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HalloWelt<\/span> <\/span>{\n    public <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> main(<span class=\"hljs-built_in\">String<\/span>&#91;] args) {\n        System.out.println(<span class=\"hljs-string\">\"Hallo aus einem nativen Image!\"<\/span>);\n    }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Kompilieren und ausf\u00fchren:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">javac HalloWelt.java\nnative-image HalloWelt\n.\/hallowelt\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Die Ausgabe erscheint nahezu sofort \u2013 messbar im zweistelligen Millisekundenbereich. Ein vergleichbarer&nbsp;<code>java HalloWelt<\/code>-Aufruf braucht auf der JVM inklusive JVM-Start 200\u2013500 ms.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Native Images mit Spring Boot<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Spring Boot 4.x unterst\u00fctzt GraalVM Native Images nativ. Die integrierte AOT-Engine von Spring Boot generiert w\u00e4hrend der Build-Phase die notwendige Konfiguration f\u00fcr Reflection, Ressourcen und Proxys automatisch.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Maven-Konfiguration<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">plugin<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.graalvm.buildtools<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>native-maven-plugin<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version<\/span>&gt;<\/span>0.11.0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">version<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">plugin<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Bauen des nativen Images:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-tag\">mvn<\/span> <span class=\"hljs-selector-tag\">-Pnative<\/span> <span class=\"hljs-selector-tag\">native<\/span><span class=\"hljs-selector-pseudo\">:compile<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Die resultierende Binary liegt im&nbsp;<code>target\/<\/code>-Verzeichnis. Ein typischer Spring Boot REST-Controller startet damit in unter 0,1 Sekunden \u2013 ein beeindruckender Unterschied zu den 3\u20135 Sekunden auf der klassischen JVM.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Docker-Image mit Native Image<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ein Dockerfile f\u00fcr das native Image f\u00e4llt minimal aus:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">FROM debian:bookworm-slim\nCOPY target\/meine-anwendung \/app\nENTRYPOINT &#91;<span class=\"hljs-string\">\"\/app\"<\/span>]\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Das resultierende Docker-Image wiegt oft unter 20 MB, w\u00e4hrend ein herk\u00f6mmliches Eclipse-Temurin-Image bei \u00fcber 200 MB liegt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Reflection-Konfiguration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Da GraalVM eine Closed-World-Annahme trifft, m\u00fcssen Reflection-Zugriffe explizit deklariert werden. Spring Boot generiert diese Konfiguration automatisch w\u00e4hrend der AOT-Phase. Bei manuellen Projekten oder wenn Bibliotheken dynamisch auf Klassen zugreifen, erstellt man eine&nbsp;<code>META-INF\/native-image\/reflect-config.json<\/code>&nbsp;im Ressourcenverzeichnis:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JSON \/ JSON mit Kommentaren\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">&#91;\n  {\n    <span class=\"hljs-attr\">\"name\"<\/span>: <span class=\"hljs-string\">\"com.example.MyService\"<\/span>,\n    <span class=\"hljs-attr\">\"methods\"<\/span>: &#91;\n      { <span class=\"hljs-attr\">\"name\"<\/span>: <span class=\"hljs-string\">\"findAll\"<\/span>, <span class=\"hljs-attr\">\"parameterTypes\"<\/span>: &#91;] }\n    ]\n  }\n]\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON mit Kommentaren<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Weitere Konfigurationsdateien sind&nbsp;<code>resource-config.json<\/code>&nbsp;(f\u00fcr Ressourcen wie&nbsp;<code>.properties<\/code>&nbsp;oder Templates) und&nbsp;<code>proxy-config.json<\/code>&nbsp;(f\u00fcr dynamische Proxys). Viele moderne Bibliotheken wie Jackson, Hibernate oder Spring Boot liefern mittlerweile automatisch generierte Konfiguration aus.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Performance-Vergleich: JVM vs. Native Image<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th class=\"has-text-align-left\" data-align=\"left\">Kriterium<\/th><th class=\"has-text-align-left\" data-align=\"left\">Klassische JVM<\/th><th class=\"has-text-align-left\" data-align=\"left\">Native Image<\/th><\/tr><\/thead><tbody><tr><td>Startzeit (Hello World)<\/td><td>~300 ms<\/td><td>~5 ms<\/td><\/tr><tr><td>Startzeit (Spring Boot)<\/td><td>~4 s<\/td><td>~80 ms<\/td><\/tr><tr><td>Speicher (Spring Boot)<\/td><td>~150 MB<\/td><td>~25 MB<\/td><\/tr><tr><td>Docker-Image-Gr\u00f6\u00dfe<\/td><td>~250 MB<\/td><td>~15 MB<\/td><\/tr><tr><td>Build-Zeit<\/td><td>~10 s<\/td><td>~2\u20133 min<\/td><\/tr><tr><td>Reflection<\/td><td>uneingeschr\u00e4nkt<\/td><td>konfigurationspflichtig<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Insbesondere f\u00fcr kurze Prozesse wie Serverless Functions oder CLI-Tools, die nur wenige Sekunden laufen, sind die Startzeit- und Speichervorteile entscheidend. Die JVM w\u00fcrde hier die meiste Zeit mit dem Start verbringen, w\u00e4hrend das native Image sofort produktiv arbeitet.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GraalVM Native Images l\u00f6sen die klassischen Schwachstellen von Java \u2013 Startzeit und Speicherverbrauch \u2013 effektiv. Eine einfache&nbsp;<code>hello world<\/code>-Binary startet in unter 10 ms, ein kompletter Spring Boot REST-Service in unter 100 ms. Der Preis ist eine l\u00e4ngere Build-Zeit von mehreren Minuten und die Closed-World-Annahme, die dynamische Features einschr\u00e4nkt. F\u00fcr Cloud-native Anwendungen, Serverless-Deployments (AWS Lambda, Google Cloud Functions) und CLI-Tools sind Native Images die nat\u00fcrliche Wahl. Mit der zunehmenden Integration in Frameworks wie Spring Boot, Micronaut und Quarkus wird der Einstieg immer einfacher \u2013 besonders die automatische AOT-Konfiguration von Spring Boot senkt die Einstiegsh\u00fcrde massiv.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Java ist bekannt f\u00fcr seine Plattformunabh\u00e4ngigkeit: Einmal kompilierter Bytecode l\u00e4uft auf jeder JVM. Doch dieser Komfort hat seinen Preis \u2013 die JVM muss zur Laufzeit starten, Klassen laden, Bytecode interpretieren und zur Laufzeit optimieren (JIT). F\u00fcr Microservices, Serverless Functions oder CLI-Tools kann dieser Overhead st\u00f6rend sein, besonders in Container-Umgebungen, wo kurze Startzeiten und geringer Speicherverbrauch [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,5],"tags":[],"class_list":["post-649","post","type-post","status-publish","format-standard","hentry","category-plain_java","category-spring"],"_links":{"self":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/649","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=649"}],"version-history":[{"count":1,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/649\/revisions"}],"predecessor-version":[{"id":651,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/649\/revisions\/651"}],"wp:attachment":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}