{"id":2798,"date":"2022-02-18T10:12:25","date_gmt":"2022-02-18T10:12:25","guid":{"rendered":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/?p=2798"},"modified":"2022-06-16T12:26:57","modified_gmt":"2022-06-16T12:26:57","slug":"graalvm-ein-blick-auf-die-performance","status":"publish","type":"post","link":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/","title":{"rendered":"GraalVM \u2013 Ein Blick auf die Performance"},"content":{"rendered":"\n<p>Die GraalVM ist nun seit reichlich zwei Jahren am Markt. Sie verspricht im Wesentlichen zwei Dinge: Bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen.<\/p>\n\n\n\n<p>Dieser Blogbeitrag konzentriert sich auf die Performance. Dabei soll es aber nicht prim\u00e4r darum gehen, ob und wieviel ein spezielles Programm auf dem Graal-JDK schneller ausgef\u00fchrt wird als auf einem herk\u00f6mmlichen JDK. Die konkreten Messwerte und die relativen Vergleiche sind ohnehin nicht nur vom untersuchten Programm abh\u00e4ngig und wenig verallgemeinerbar, sondern auch nur Momentaufnahmen: Sowohl die GraalVM als auch beispielsweise das OpenJDK werden best\u00e4ndig weiterentwickelt, so dass sich auch die Messwerte stets \u00e4ndern werden. Stattdessen besch\u00e4ftigt sich der Blogbeitrag vor allem mit den Fragen: Warum sollte die GraalVM wesentlich performanter sein? Was macht sie anders als die herk\u00f6mmlichen JDKs? Damit wird eine Absch\u00e4tzung m\u00f6glich, ob alle Programme performanter ausgef\u00fchrt werden oder keine nennenswerte Steigerung zu erwarten ist oder ob die Performance-Steigerung nur in bestimmten Anwendungsf\u00e4llen zu erwarten ist. Und letztendlich, ob das \u201eherk\u00f6mmliche\u201c Java demzufolge zu langsam ist\u2026<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"682\" src=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-1024x682.jpg\" alt=\"Laptop verbunden mit LED Display, auf dem &quot;Hello World&quot; abgebildet wird. \" class=\"wp-image-2906\" srcset=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-1024x682.jpg 1024w, https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-600x400.jpg 600w, https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-768x512.jpg 768w, https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-1536x1024.jpg 1536w, https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-2048x1365.jpg 2048w, https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-640x427.jpg 640w, https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-1200x800.jpg 1200w, https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-1920x1280.jpg 1920w\" sizes=\"auto, (max-width: 639px) 98vw, (max-width: 1199px) 64vw, 770px\" \/><\/figure>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><strong><strong>Entwicklung der Compiler<\/strong><\/strong><\/strong><\/h2>\n\n\n\n<p>Da die Performance eines Java-Programms wesentlich durch den Compiler bestimmt wird und auch hier die Kernfrage ist, was die GraalVM anders macht verschaffen wir uns zun\u00e4chst einmal einen \u00dcberblick \u00fcber Compiler.<\/p>\n\n\n\n<p>In den Anf\u00e4ngen der Programmierung existierte zun\u00e4chst noch gar kein Compiler \u2013 es wurde direkt der Maschinencode programmiert. Da dies un\u00fcbersichtlich und wenig verst\u00e4ndlich war, hat sich zeitnah der Assembler-Code entwickelt. Dabei handelt es sich jedoch im Wesentlichen um eine direkte Abbildung des Maschinencodes \u2013 nur dass statt Bin\u00e4r- oder Hexadezimal-Opcodes nun Buchstabenk\u00fcrzel verwendet werden. Auch hier sprechen wir (zumindest im Scope dieses Blogbeitrags) noch nicht von einer Programmiersprache und einem Compiler.<\/p>\n\n\n\n<p>Mit der Zeit wurde die Entwicklung immer komplizierterer Programme notwendig. Damit wurde der Assembler-Code zunehmend unpraktikabel. Daher wurden in den 1950er Jahren die ersten h\u00f6heren Programmiersprachen entwickelt. Diese ben\u00f6tigten einen Compiler, der den Quelltext in Maschinencode \u00fcbersetzt.<\/p>\n\n\n\n<p>Dies war zun\u00e4chst der klassische AOT-Compiler (AOT: Ahead Of Time). Der Quelltext wird analysiert (Syntaxanalyse), in eine interne Baumstruktur \u00fcberf\u00fchrt (Syntaxbaum) und aus diesem wird Maschinencode generiert (Codegenerierung). Die entstehende Bin\u00e4rdatei kann nun direkt ausgef\u00fchrt werden.<\/p>\n\n\n\n<p>Alternativ zur AOT-Kompilierung kann ein Programm auch durch einen Interpreter ausgef\u00fchrt werden. Hierbei wird der Quelltext eingelesen und zeilenweise durch den Interpreter umgesetzt. Die eigentlichen Operationen (z. B. Addition, Vergleich, Programmausgabe) f\u00fchrt dann der Interpreter aus.<\/p>\n\n\n\n<p>Der AOT-Compiler hat gegen\u00fcber dem Interpreter den Vorteil, dass die Programme wesentlich schneller ausgef\u00fchrt werden. Allerdings sind die erzeugten Bin\u00e4rdateien maschinenabh\u00e4ngig. Dar\u00fcber hinaus hat der Interpreter bessere Fehleranalysem\u00f6glichkeiten, da er beispielsweise Zugriff auf Laufzeitinformationen hat.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><strong><strong>Java, Interpreter und JIT-Compiler<\/strong><\/strong><\/strong><\/h2>\n\n\n\n<p>Beim Entwurf der Programmiersprache Java war ein Ziel, dass sie architekturneutral und portabel ist. Aus diesem Grund wurde der Java-Quellcode von Anfang an in maschinenunabh\u00e4ngigen Bytecode \u00fcbersetzt. Dieser konnte dann von einer Laufzeitumgebung, der JRE (Java Runtime Environment), interpretiert werden. Damit war der \u00fcbersetzte Bytecode maschinenunabh\u00e4ngig. So konnten beispielsweise Applets ohne Anpassungen auf einem Windows-PC, einem Mac oder einer Unix-Workstation ausgef\u00fchrt werden. Die JRE muss dazu \u2013 unabh\u00e4ngig vom Applet \u2013 vorab auf den Workstations installiert sein.<\/p>\n\n\n\n<p>Diese Mischform \u2013 AOT bis zum Bytecode, dann Interpretation zur Laufzeit \u2013 ist \u00fcbrigens keine Idee aus der Java-Welt: Bereits in den 1970er Jahren hat beispielsweise Pascal den p-Code genutzt. [1]<\/p>\n\n\n\n<p>Als die Java-Technologie im Jahre 1995 ver\u00f6ffentlicht wurde [2], war diese Maschinenunabh\u00e4ngigkeit zun\u00e4chst mit gro\u00dfen Performance-Einbu\u00dfen verbunden. Viele der damals relevanten Programmiersprachen wie bspw. \u201eC\u201c kompilieren ihren Quellcode direkt in Maschinencode (AOT). Dieser kann auf dem entsprechenden System nativ ausgef\u00fchrt werden und ist somit wesentlich performanter als die Interpretation des Bytecodes. Zu jener Zeit hat sich in den K\u00f6pfen vieler IT-Fachkr\u00e4fte die Grundeinstellung \u201eJava ist langsam\u201c festgesetzt \u2013 damals zu Recht.<\/p>\n\n\n\n<p>Nun ist aber ein weiteres Entwurfsziel der Programmiersprache Java die hohe Leistungsf\u00e4higkeit. Aus diesem Grund wurde im Jahr 1998 der JIT-Compiler (JIT: Just In Time) eingef\u00fchrt [2]. Damit wurden die Performance-Einbu\u00dfen durch die reine Interpretation stark reduziert.<\/p>\n\n\n\n<p>Bei der JIT-Kompilierung wird der Bytecode beim Programmstart zun\u00e4chst ebenfalls interpretiert. Allerdings wird dabei genau verfolgt, welche Programmteile wie oft ausgef\u00fchrt werden. Die h\u00e4ufig ausgef\u00fchrten Programmteile werden nun \u2013 zur Laufzeit \u2013 in Maschinencode \u00fcbersetzt. Zuk\u00fcnftig werden diese Programmteile nicht mehr interpretiert, sondern es wird der native Maschinencode ausgef\u00fchrt. Hier wird somit zun\u00e4chst Ausf\u00fchrungszeit f\u00fcr die Kompilierung \u201einvestiert\u201c, um bei jedem zuk\u00fcnftigen Aufruf dann Ausf\u00fchrungszeit einsparen zu k\u00f6nnen.<\/p>\n\n\n\n<p>Die JIT-Kompilierung ist daher ein Mittelweg zwischen AOT-Kompilierung und Interpretation. Die Plattformunabh\u00e4ngigkeit bleibt erhalten, da der Maschinencode erst zur Laufzeit erzeugt wird. Und da die h\u00e4ufig genutzten Programmteile nach einer gewissen Warmlauf-Zeit als nativer Maschinencode ausgef\u00fchrt werden, ist auch die Performance (dann) ann\u00e4hernd so gut wie bei der AOT-Kompilierung. Ganz allgemein gilt dabei: Je h\u00e4ufiger einzelne Programmteile ausgef\u00fchrt werden, desto mehr k\u00f6nnen die anderen, interpretierten Programmteile bei der Performance-Betrachtung vernachl\u00e4ssigt werden. Und dies gilt vor allem f\u00fcr oft durchlaufene Schleifen oder langlaufende Server-Anwendungen, deren Methoden st\u00e4ndig aufgerufen werden.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><strong><strong>Laufzeitoptimierungen<\/strong><\/strong><\/strong><\/h2>\n\n\n\n<p>Mit den bisher betrachteten Mechanismen ist der JIT-Compiler zwar plattformunabh\u00e4ngig \u2013 kann sich aber an die Ausf\u00fchrungszeit des AOT-Compilers nur herantasten, sie jedoch nicht erreichen oder gar \u00fcbertreffen. Aus diesem Grund war zu dem Zeitpunkt, als der JIT-Compiler in das JDK integriert wurde, noch keineswegs sicher, dass es f\u00fcr einen Siegeszug ausreichen wird.<\/p>\n\n\n\n<p>Allerdings hat der JIT-Compiler einen gro\u00dfen Vorteil gegen\u00fcber dem AOT-Compiler: Er ist nicht nur auf die statische Quellcode-Analyse angewiesen, sondern er kann das Programm direkt zur Laufzeit beobachten. Da sich die allermeisten Programme in Abh\u00e4ngigkeit von Eingaben und\/oder Umgebungszust\u00e4nden unterschiedlich verhalten, kann der JIT-Compiler zur Laufzeit wesentlich zielgenauer optimieren.<\/p>\n\n\n\n<p>Ein gro\u00dfer Pluspunkt sind dabei die spekulativen Optimierungen. Dabei werden Annahmen getroffen, welche in den meisten F\u00e4llen zutreffen. Damit das Programm trotzdem korrekt funktioniert, wird die Annahme mit einem sogenannten \u201eGuard\u201c abgesichert. Beispielsweise geht die JVM davon aus, dass in einem produktiv ausgef\u00fchrten Programm die Polymorphie so gut wie nicht genutzt wird. Nat\u00fcrlich ist die Polymorphie grunds\u00e4tzlich sinnvoll, aber die praktischen Einsatzszenarien beschr\u00e4nken sich meist auf den Testbereich oder auf die Entkopplung einer Codebasis \u2013 \u00fcblicherweise zur Nutzung durch verschiedene Programme oder f\u00fcr zuk\u00fcnftige Erweiterbarkeit. W\u00e4hrend der Laufzeit eines konkreten produktiven Programmes \u2013 und dies ist der Scope der JVM \u2013 wird die Polymorphie jedoch selten genutzt. Das Problem ist dabei, dass es beim Aufruf einer Interface-Methode relativ zeitaufwendig ist, f\u00fcr das vorhandene Objekt die passende Methoden-Implementierung herauszusuchen. Aus diesem Grund werden die Methodenaufrufe getrackt. Wird beispielsweise mehrmals die Methode \u201ejava.util.List.add(\u2026)\u201c auf einem Objekt vom Typ \u201ejava.util.ArrayList\u201c aufgerufen, merkt sich die JVM dies. Bei den folgenden Methodenaufrufen \u201eList::add\u201c wird darauf spekuliert, dass es sich wieder um eine ArrayList handelt. Zun\u00e4chst wird mit einem Guard die Annahme abgesichert: Es wird gepr\u00fcft, dass das Objekt tats\u00e4chlich vom Typ ArrayList ist. \u00dcblicherweise trifft dies zu und die bereits mehrfach ermittelte Methode wird mittels der \u201egemerkten\u201d Referenz einfach direkt aufgerufen.<\/p>\n\n\n\n<p>Seit der Integration des JIT-Compilers in das JDK sind nun mittlerweile mehr als zwei Jahrzehnte vergangen. In dieser Zeit wurden sehr viele Laufzeitoptimierungen integriert. Die vorgestellte Polymorphie-Spekulation ist nur ein kleines Beispiel, welches verdeutlichen soll: Neben der Kompilierung von Maschinencode wurden sehr viele Optimierungen erdacht, welche in einer komplexen Sprache wie Java nur zur Laufzeit funktionieren. Wenn eine Instanz beispielsweise mittels Reflection erzeugt wird, ist es f\u00fcr einen AOT-Compiler sehr schwer bis unm\u00f6glich, den konkreten Typ zu bestimmen und die spekulative Optimierung umzusetzen. Der Geschwindigkeitsvorteil der aktuellen JIT-Compiler beruht demnach im Wesentlichen darauf, dass sie dem Programm bei der Ausf\u00fchrung zuschauen, Gewohnheiten erkennen und schlie\u00dflich Abk\u00fcrzungen einbauen k\u00f6nnen.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><strong><strong>GraalVM<\/strong><\/strong><\/strong><\/h2>\n\n\n\n<p>Die GraalVM ist ein JDK von Oracle, welches auf dem OpenJDK basiert. Sie bringt eine virtuelle Maschine sowie viele Entwickler-Tools mit \u2013 was soweit auch f\u00fcr die anderen JDKs gilt. Warum also erregt die GraalVM wesentlich mehr Aufmerksamkeit als die anderen JDKs?<\/p>\n\n\n\n<p>Zun\u00e4chst bringt die GraalVM einen GraalVM-Compiler mit, welcher in Java entwickelt wurde. Dar\u00fcber hinaus soll auch die gesamte JVM in Java umgeschrieben werden. Im letzten Kapitel wurde dargelegt, dass die aktuellen JVMs vor allem deswegen sehr performant sind, weil jahrzehntelang verschiedenste Optimierungen erg\u00e4nzt wurden, welche sich nun summieren. Diese Optimierungen sind vor allem Java-spezifisch. Sie werden zumeist von Leuten entwickelt, welche einen Java-Background besitzen. Wenn nun die Ausf\u00fchrungsumgebung nicht in C++ sondern in Java umgesetzt ist, sind folglich keine C++-Kenntnisse mehr notwendig, um Optimierungen beizusteuern. Die Entwickler-Community wird so mittel- bis langfristig auf eine breitere Basis gestellt.<\/p>\n\n\n\n<p>Ein weiterer spannender Aspekt der GraalVM ist, dass sie nicht nur Java-basierte Sprachen unterst\u00fctzt. Das \u201cTruffle Language Implementation Framework\u201d ist ein Ansatzpunkt f\u00fcr die Entwicklung eigener Sprachen (DSL). Der GraalVM-Compiler unterst\u00fctzt mit dem Truffle-Framework entwickelte Sprachen, so dass diese auch in der GraalVM ausgef\u00fchrt werden k\u00f6nnen und von allen Vorteilen entsprechend profitieren. Einige Sprachen wie JavaScript, Python oder Ruby werden dabei bereits von Haus aus seitens der GraalVM unterst\u00fctzt. Da alle Truffle-Sprachen gleichzeitig und gemeinsam in der GraalVM ausgef\u00fchrt werden k\u00f6nnen, wird hier auch von einer polyglotten VM gesprochen.<\/p>\n\n\n\n<p>Dar\u00fcber hinaus werden auch LLVM-basierte Sprachen unterst\u00fctzt. Bei LLVM handelt es sich um ein Rahmenprojekt f\u00fcr optimierende Compiler [4][5]. Dabei werden nicht nur Compiler-Bestandteile und -Technologien f\u00fcr externe Compiler-Entwicklungen bereitgestellt, sondern es werden auch im LLVM-Projekt bereits Compiler f\u00fcr viele Programmiersprachen wie bspw. C\/C++ oder Fortran angeboten. Die LLVM Runtime ist ein weiterer Bestandteil der GraalVM, mit welchem LLVM-basierte Sprachen aufbauend auf dem Truffle-Framework in der GraalVM ausgef\u00fchrt werden k\u00f6nnen. Auf den polyglotten Aspekt soll hier aber nicht weiter eingegangen werden, denn er h\u00e4tte seinen eigenen Blogbeitrag verdient.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\"><strong><strong>GraalVM Native Image<\/strong><\/strong><\/h3>\n\n\n\n<p>Die f\u00fcr diesen Beitrag relevanteste Neuerung ist die sogenannte \u201eNative-Image-Technologie\u201c. Das native-image ist ein Entwickler-Tool der GraalVM. Es erzeugt aus Bytecode eine ausf\u00fchrbare Datei. Die Ziele sind eine bessere Performance und weniger Hauptspeichernutzung zur Laufzeit. Nun wurde aber bisher beschrieben, dass Java immer schneller geworden ist: Der JIT-Compiler \u00fcbersetzt alle h\u00e4ufig ausgef\u00fchrten (d. h. relevanten) Programmteile in nativen Maschinencode. Das Programm wird w\u00e4hrend der Ausf\u00fchrung beobachtet und es werden fortlaufend Laufzeitoptimierungen vorgenommen. Damit stellt sich also die Frage: Was kann denn hier noch um Gr\u00f6\u00dfenordnungen verbessert werden?<\/p>\n\n\n\n<p>Die Antwort ist verbl\u00fcffend einfach: Die Startzeit. Auch mit JIT-Compilern wird der Bytecode zun\u00e4chst interpretiert. Erstens ist der Programmstart meistens kein h\u00e4ufig ausgef\u00fchrter Programmteil. Zweitens m\u00fcssen diese Programmteile \u00fcblicherweise erst ein paarmal durchlaufen werden, damit der JIT-Compiler diese als lohnendes \u00dcbersetzungsziel erkennt. Mit den Laufzeitoptimierungen verh\u00e4lt es sich analog: Das Programm muss erst einmal zur Laufzeit beobachtet werden, damit die passenden Optimierungen erkannt und eingebaut werden k\u00f6nnen. An dieser Stelle kommt versch\u00e4rfend hinzu, dass beim Start alle ben\u00f6tigten Objekte sowie deren Klassen inklusive der kompletten Vererbungshierarchie initialisiert werden m\u00fcssen.<\/p>\n\n\n\n<p>Da wir nun eine Vorstellung vom \u201eWas\u201c haben, interessiert uns das \u201eWie\u201c: Wie kann unser Programm dazu bewegt werden, schneller zu starten?<\/p>\n\n\n\n<p>Bei der Erstellung des Native Image wird der Bytecode zun\u00e4chst sehr umfangreich statisch analysiert. Unter anderem wird dabei gepr\u00fcft, welche Code-Teile zur Laufzeit \u00fcberhaupt ausgef\u00fchrt werden k\u00f6nnen. Dies bezieht sich nicht nur auf die vom Nutzer bereitgestellten Klassen, sondern auf den gesamten Klassenpfad \u2013 also inklusive der von der JVM bereitgestellten Java-Klassenbibliotheken. Nur die ermittelten Quellcode-Fragmente werden in das Native Image aufgenommen. Somit wird an dieser Stelle zwar der Umfang stark reduziert \u2013 aber es wird auch eine \u201eClosed world assumption\u201c aufgestellt: Sobald irgendetwas dynamisch zur Laufzeit geladen wird, steht das Native Image Tool vor einem Problem. Es erkennt nicht, dass auch diese Quellcode-Teile ausgef\u00fchrt werden k\u00f6nnen und damit ben\u00f6tigt werden. Aus diesem Grund wird auf diese Weise nicht viel mehr als ein einfaches HelloWorld-Programm funktionieren. Deshalb kann und muss man bei der Erstellung des Native Image dem Tool noch Informationen mitgeben, was alles dynamisch aufgerufen werden kann.<\/p>\n\n\n\n<p>Nach der statischen Analyse wird nun der erste Punkt umgesetzt, welcher die Startgeschwindigkeit erh\u00f6ht: Da der JIT-Compiler mit der Interpretation starten w\u00fcrde, wird ein AOT-Compiler genutzt, um Maschinencode zu erstellen. Das erzeugte Native Image ist, wie der Name schon impliziert, nativ ausf\u00fchrbarer Maschinencode. Damit geht allerdings die Plattformunabh\u00e4ngigkeit verloren.<\/p>\n\n\n\n<p>Zus\u00e4tzlich zum nativ kompilierten Programm wird die sogenannte SubstrateVM in das Native Image aufgenommen. Hierbei handelt es sich um eine abgespeckte VM, welche nur die zur Ausf\u00fchrung des Native Image notwendigen Komponenten wie beispielsweise Thread Scheduling oder Garbage Collection enth\u00e4lt. Die SubstrateVM hat dabei auch Limitierungen, beispielsweise ist die Unterst\u00fctzung eines Security Managers gar nicht vorgesehen.<\/p>\n\n\n\n<p>Eine zus\u00e4tzliche Steigerung der Startgeschwindigkeit wird erreicht, indem das Native Image bei der Erstellung bereits vorab initialisiert wird. Das Programm wird dazu nach dem Kompilieren soweit gestartet, bis die wesentlichen Initialisierungen erfolgt sind, aber noch kein Input von au\u00dfen verarbeitet werden muss. Von diesem gestarteten Zustand wird ein Speicherabbild erstellt und in das Native Image gelegt.<\/p>\n\n\n\n<p>An das \u201eWas\u201c und das \u201eWie\u201c schlie\u00dft sich nun noch ein eher kritisches \u201eWarum\u201c an: Der AOT-Compiler ist schon ein halbes Jahrhundert bekannt und Java existiert mittlerweile auch seit einem Vierteljahrhundert. Vor allem in der Anfangszeit von Java wurden verschiedene AOT-Ans\u00e4tze ausprobiert \u2013 welche sich jedoch nicht durchsetzen konnten. Warum sollte es jetzt anders sein? Warum wird ausgerechnet jetzt eine geringere Startzeit interessant, wenn sie doch mit Nachteilen verbunden ist? Warum sind die performanten Antwortzeiten im darauffolgenden tage- oder wochenlangen Betrieb pl\u00f6tzlich weniger wichtig?<\/p>\n\n\n\n<p>Die Antwort ist im Cloud Computing zu suchen: Hier werden die Services in einer anderen Form zur Verf\u00fcgung gestellt. Bisher wurden die Services vorwiegend in einem Anwendungscontainer betrieben, welcher Tag und Nacht ausgef\u00fchrt wird und in welchem das Programm schon seit Tagen komplett durchoptimiert? ist. Schlie\u00dflich wurde der Anwendungscontainer \u00fcblicherweise auch bei sp\u00e4rlicher Nutzung (z. B. Tageszeiten-abh\u00e4ngig) nicht heruntergefahren. Im Gegensatz dazu kann in der Cloud die Service-Infrastruktur bei Nicht-Nutzung problemlos heruntergefahren werden \u2013 somit k\u00f6nnen Kapazit\u00e4ten gespart werden. Beim n\u00e4chsten Aufruf wird die Infrastruktur wieder hochgefahren und der Aufruf wird ausgef\u00fchrt. Das bedeutet, dass die Programme in der Cloud nicht im Dauerbetrieb laufen, sondern dass es sich unter Umst\u00e4nden bei jedem Aufruf um einen Kaltstart handelt. Demzufolge ist die Startzeit hier \u201eauf einmal\u201c sehr entscheidend. Und da zu erwarten ist, dass zuk\u00fcnftig noch mehr Java-Programme in der Cloud statt in einem Anwendungscontainer ausgef\u00fchrt werden, wird sich die Fokussierung auf die Startzeit wohl noch verst\u00e4rken.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><strong><strong>Hands On: HelloWorld<\/strong><\/strong><\/strong><\/h2>\n\n\n\n<p>Nach der ganzen Theorie m\u00f6chten wir dem JDK nun bei der Arbeit zuschauen. Dazu kommt zun\u00e4chst die in Listing 1 dargestellte Klasse \u201eHelloWorld\u201c zum Einsatz.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">package de.zeiss.zdi.graal;\n\npublic class HelloWorld {\n\n    public static void main(String[] args) {\n        System.out.println(\"Moin!\");\n    }\n\n}<\/pre>\n\n\n\n<p><em>Listing 1<\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Zun\u00e4chst die klassische Variante: Wir befinden uns auf einer Linux-VM und es ist ein OpenJDK installiert:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">> java --version\n\nopenjdk 11.0.11 2021-04-20\nOpenJDK Runtime Environment (build 11.0.11+9-Ubuntu-0ubuntu2.20.04)\nOpenJDK 64-Bit Server VM (build 11.0.11+9-Ubuntu-0ubuntu2.20.04, mixed mode, sharing)\n\njava 11.0.12 2021-07-20 LTS\nJava(TM) SE Runtime Environment GraalVM EE 21.2.0.1 (build 11.0.12+8-LTS-jvmci-21.2-b08)\nJava HotSpot(TM) 64-Bit Server VM GraalVM EE 21.2.0.1 (build 11.0.12+8-LTS-jvmci-21.2-b08, mixed mode, sharing)<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Mit diesem Setup kompilieren wir die HelloWorld-Klasse (javac) und f\u00fchren den entstandenen Bytecode auf einer JVM aus:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">time java -cp target\/classes de.zeiss.zdi.graal.HelloWorld<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Damit erhalten wir folgende Ausgabe:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Moin!\n\nreal    0m0.055s\nuser    0m0.059s\nsys     0m0.010s<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>F\u00fcr die Auswertung ist hier die Summe der beiden Zeilen &#8222;user&#8220; und &#8222;sys&#8220; relevant. Das ist die Rechenzeit, die f\u00fcr die Programmausf\u00fchrung notwendig war \u2013 in dem Fall also ca. 69ms.<\/p>\n\n\n\n<p>Eine Anmerkung zu den 55ms: Das HelloWorld-Programm hat vom Start bis zu seiner Beendigung 55ms \u201eReal-Zeit\u201c (die Zeit, welche der Nutzer wahrnimmt) ben\u00f6tigt. Das ist weniger als die 69ms ben\u00f6tigte Rechenzeit. Dies liegt daran, dass das eingesetzte Linux-System mehrere Prozessoren besitzt. F\u00fcr unsere Messungen soll hier aber die vom System aufgewandte Rechenzeit betrachtet werden. Erstens ist die Rechenzeit unabh\u00e4ngiger davon, wie viele Prozessoren das Programm ausgef\u00fchrt haben. Und zweitens ist dies in der Cloud beispielsweise auch die Zeit, welche der Anwendungsbetreiber bezahlen muss.<\/p>\n\n\n\n<p>Nun sind wir auf die GraalVM gespannt. Auf deren Homepage [3] wird sie zum Download angeboten. F\u00fcr unsere Evaluation ist die Enterprise-Variante (&#8222;Free for evaluation and development&#8220;) passend, da ein Gro\u00dfteil der Performance-Optimierungen nur hier enthalten sind.<\/p>\n\n\n\n<p>Die Installation ist f\u00fcr Linux sehr gut dokumentiert und funktioniert nahezu problemlos. Damit ist die GraalVM als JDK nutzbar.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">> java --version\n\njava version \"11.0.12\" 2021-07-20 LTS\nJava(TM) SE Runtime Environment GraalVM EE 21.2.0.1 (build 11.0.12+8-LTS-jvmci-21.2-b08)\nJava HotSpot(TM) 64-Bit Server VM GraalVM EE 21.2.0.1 (build 11.0.12+8-LTS-jvmci-21.2-b08, mixed mode, sharing)<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Unser HelloWorld-Programm k\u00f6nnen wir nun genauso mit dem GraalJDK kompilieren (javac) und ausf\u00fchren. Damit erhalten wir die folgende Ausgabe:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Moin!\n\nreal    0m0.084s\nuser    0m0.099s\nsys     0m0.017s<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Interessanterweise ben\u00f6tigt die JVM des GraalJDK nahezu 70 % mehr Rechenzeit, um unser HelloWorld-Beispiel als Bytecode auszuf\u00fchren. Nun verspricht die GraalVM den signifikanten Performance-Vorteil allerdings auch nicht prim\u00e4r bei der Ausf\u00fchrung von Bytecode, sondern bei Nutzung der Native-Image-Technologie.<\/p>\n\n\n\n<p>Das native-image (das Entwickler-Tool) ist in der heruntergeladenen GraalVM noch nicht enthalten, daf\u00fcr existiert aber das Kommandozeilen-Tool \u201egu\u201c (GraalVM Updater). Mit diesem kann man zus\u00e4tzliche Komponenten nachladen, verwalten und aktualisieren. Auch hier unterst\u00fctzt die Dokumentation der GraalVM sehr gut. Mit dem nachgeladenen Entwickler-Tool k\u00f6nnen wir nun aus dem Bytecode das Native Image erzeugen. Im Fall eines so trivialen Programms wie unserem HelloWorld-Beispiel gen\u00fcgt dazu ein einfacher Kommandozeilenbefehl mit dem vollqualifizierten Klassennamen als Argument:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">cd ~\/dev\/prj\/graal-eval\/target\/classes\nnative-image de.zeiss.zdi.graal.HelloWorld<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Die Erstellung des HelloWorld Native Image ben\u00f6tigt reichlich 3 Minuten Rechenzeit \u2013 und das ausf\u00fchrbare Programm ist ca. 12 MB gro\u00df. Auf den ersten Blick vergleicht man die Gr\u00f6\u00dfe vermutlich mit dem Bytecode: Die HelloWorld.class ist lediglich 565 Byte gro\u00df. Allerdings enth\u00e4lt das Native Image nicht nur die kompilierte Klasse, sondern alle relevanten Teile der Java-Klassenbibliothek sowie die SubstrateVM. Verglichen mit der Gr\u00f6\u00dfe einer JRE liegt das Native Image nur noch bei grob gesch\u00e4tzt 10 %.<\/p>\n\n\n\n<p>Doch zur\u00fcck zu unserem Native Image: Wir haben es erfolgreich erstellt, k\u00f6nnen es ausf\u00fchren und erhalten dabei die folgende Ausgabe.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">time .\/de.zeiss.zdi.graal.helloworld<\/pre>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Moin!\n\nreal    0m0.004s\nuser    0m0.003s\nsys     0m0.001s<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Dieses Resultat k\u00f6nnen wir erst einmal als relevanten Geschwindigkeitsgewinn stehen lassen.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><strong><strong>Hands On: HelloScript<\/strong><\/strong><\/strong><\/h2>\n\n\n\n<p>Bei der GraalVM wird immer wieder herausgestellt, dass es sich dabei nicht nur um eine Java-VM sondern um eine polyglotte VM handelt. Aus diesem Grund erweitern wir unser HelloWorld-Programm noch um einen kleinen Exkurs in die JavaScript-Welt. Der Quellcode dazu ist in Listing 2 dargestellt. Der wesentliche Unterschied ist hier der notwendige \u00dcbergang von der Java- in die JavaScript-Welt.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">package de.zeiss.zdi.graal;\n\nimport javax.script.ScriptEngine;\nimport javax.script.ScriptEngineManager;\nimport javax.script.ScriptException;\n\npublic class HelloScriptEngine {\n\n    public static void main(String[] args) throws ScriptException {\n\n        ScriptEngine jsEngine = new ScriptEngineManager().getEngineByName(\"javascript\");\n\n        System.out.print(\"Hello \");\n        jsEngine.eval(\"print('JavaScript!')\");\n    }\n\n}<\/pre>\n\n\n\n<p><em> Listing 2<\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Neben dieser universellen JavaScript-Anbindung mittels der javax.script.ScriptEngine wollen wir auch die Graal-spezifische JavaScript-Anbindung ausprobieren. Dazu nutzen wir org.graalvm.polyglot.Context. Der Quelltext ist in Listing 3 dargestellt.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">package de.zeiss.zdi.graal;\n\nimport org.graalvm.polyglot.Context;\n\npublic class HelloScriptPolyglot {\n\n    public static void main(String[] args) {\n\n        System.out.print(\"Hello \");\n        try (Context context = Context.create()) {\n            context.eval(\"js\", \"print('JavaScript!')\");\n        }\n    }\n\n}<\/pre>\n\n\n\n<p> <em>Listing 3<\/em><\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Die beiden HelloScript-Programme werden analog dem HelloWorld-Programm in Bytecode \u00fcbersetzt. Bei der Erstellung der Native Images muss das Entwickler-Tool noch informiert werden, dass die JavaScript-Welt genutzt werden wird. Dies geschieht mit dem folgenden Aufruf:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">cd ~\/dev\/prj\/graal-eval\/target\/classes\nnative-image --language:js de.zeiss.zdi.graal.HelloScriptEngine\nnative-image --language:js de.zeiss.zdi.graal.HelloScriptPolyglot<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Anschlie\u00dfend kann der Bytecode auf den VMs oder die Native Images nativ ausgef\u00fchrt werden. Da das HelloScriptPolyglot Graal-spezifisch ist, k\u00f6nnen wir es allerdings nicht ohne Weiteres auf dem OpenJDK ausf\u00fchren.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><strong><strong>Ein Blick auf die Messwerte<\/strong><\/strong><\/strong><\/h2>\n\n\n\n<p>Die drei Szenarien wurden jeweils als Bytecode auf dem OpenJDK, als Bytecode auf dem GraalJDK und als Native Image ausgef\u00fchrt. Die durchschnittlichen Programmausf\u00fchrungszeiten sind in Tabelle 1 aufgef\u00fchrt.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-1 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container is-layout-flow wp-block-group-is-layout-flow\">\n<table class=\"wp-block-advgb-table advgb-table-frontend\"><tbody><tr><td><\/td><td>Hello World<\/td><td>HelloScriptEngine<\/td><td>HelloScriptPolyglot<\/td><\/tr><tr><td>Bytecode OpenJDK<\/td><td>69 ms<\/td><td>1321 ms<\/td><td>X<\/td><\/tr><tr><td>Bytecode GraalJDK<\/td><td>116 ms<\/td><td>2889 ms<\/td><td>2775 ms<\/td><\/tr><tr><td>Native Image<\/td><td>4 ms<\/td><td>13 ms<\/td><td>11 ms<\/td><\/tr><\/tbody><\/table>\n<\/div><\/div>\n\n\n\n<p><em>Tabelle 1: Beispiel f\u00fcr durchschnittliche Programmausf\u00fchrungszeiten <\/em><\/p>\n<\/div>\n<\/div>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Auf den ersten Blick f\u00e4llt ins Auge, das die Ausf\u00fchrung als Native Image in allen drei Szenarien jeweils um ein Vielfaches schneller ist als die \u00fcbliche Bytecode-Ausf\u00fchrung.<\/p>\n\n\n\n<p>Auf den zweiten Blick f\u00e4llt aber auch auf, dass die Bytecode-Ausf\u00fchrung mit dem GraalJDK wesentlich mehr Rechenzeit ben\u00f6tigt als mit dem OpenJDK: Im HelloWorld-Beispiel knapp 70 % mehr, bei dem HelloScriptEngine-Beispiel \u00fcber 100 % mehr. Das wurde von Oracle so nicht kommuniziert, ist aber grunds\u00e4tzlich auch kein allzu gro\u00dfes Problem, da die Motivation f\u00fcr den Einsatz der GraalVM vermutlich nicht in der schnelleren Bytecode-Ausf\u00fchrung liegt. Man sollte diesen Fakt aber im Hinterkopf behalten, wenn man den relevanten Speed-Up durch das Native Image ermitteln m\u00f6chte: Zur Erstellung des Native Image muss schlie\u00dflich die GraalVM installiert sein. Wenn man nun zum Vergleich die Bytecode-Ausf\u00fchrung misst und \u201ejava -jar \u2026\u201c ausf\u00fchrt, wird der Bytecode mittels der GraalVM ausgef\u00fchrt. Da im produktiven Betrieb aber bisher vermutlich eher das OpenJDK eingesetzt wurde, sollte man eher mit diesem vergleichen \u2013 und damit w\u00e4re der Speed-Up \u201enur\u201c noch reichlich halb so hoch.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong><strong><strong>Was zu bedenken ist<\/strong><\/strong><\/strong><\/h2>\n\n\n\n<p>Um die versprochenen Performance-Gewinne zu erzielen, reicht es nicht, die GraalVM anstatt eines herk\u00f6mmlichen JDKs zu installieren. Bei der Bytecode-Ausf\u00fchrung konnte \u2013 zumindest mit unseren Beispielen und unserem Setup \u2013 noch kein Performance-Gewinn erreicht werden. Dies ist erst mit einem Native Image m\u00f6glich, welches gegen\u00fcber der Bytecode-Ausf\u00fchrung jedoch auch mehrere Nachteile hat, derer man sich bewusst sein sollte.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Im Native Image wird die SubstrateVM als JVM verwendet. Diese besitzt einige Einschr\u00e4nkungen. Abgesehen davon, dass derzeit noch nicht alle Features umgesetzt sind, stehen einige Dinge wie z. B. ein Security Manager gar nicht auf der Agenda.<\/li><li>Weiterhin sollte die Dauer des Build-Prozesses beachtet werden: Beim Native Image verschwindet die Startzeit nicht einfach. Die Rechenzeit wird mit verschiedenen Ans\u00e4tzen \u201elediglich\u201c verlagert: Von der Ausf\u00fchrungszeit zur Build-Zeit. Die Erstellung unseres HelloWorld-Beispiels hat in unserer Umgebung reichlich drei Minuten ben\u00f6tigt \u2013 die Erstellung des HelloScript-Programmes dauerte bereits mehr als 20 Minuten (HelloScriptEngine: 1291s, HelloScriptPolyglot: 1251s).<\/li><li>Die gr\u00f6\u00dfte Herausforderung ist aber die \u201eClosed world assumption\u201c. Bei der Erstellung des Native Image wird eine statische Codeanalyse durchgef\u00fchrt \u2013 und nur die durchlaufenen Code-Teile werden in das Native Image kompiliert. Das funktioniert zwar f\u00fcr unser HelloWorld-Programm, aber bereits bei den JavaScript-Beispielen mussten Kommandozeilen-Parameter angegeben werden. Mittels \u201eReflection\u201c geladene Klassen werden nur erkannt, wenn der vollqualifizierte Klassenname festverdrahtet im Quellcode steht. Demzufolge gibt es Probleme mit allen Technologien, welche Dynamic Class Loading in irgendeiner Form nutzen: JNDI, JMX, \u2026<\/li><\/ul>\n\n\n\n<p>Die dynamisch geladenen Programmteile k\u00f6nnen (und m\u00fcssen) bei der Erstellung des Native Image explizit angegeben werden. Das schlie\u00dft alle Programmteile ein: Neben dem eigenen Projekt-Code sind das auch alle eingesetzten Bibliotheken \u2013 bis hin zu denen der JRE. Da diese Konfiguration f\u00fcr \u201eechte\u201c Programme eine wirkliche Herausforderung ist, werden hierzu Hilfswerkzeuge bereitgestellt, ohne die es in der Praxis vermutlich nicht funktioniert. Der Tracing Agent beispielsweise beobachtet ein als Bytecode ausgef\u00fchrtes Programm. Er erkennt alle reflektiven Zugriffe und erzeugt daraus eine JSON-Konfiguration. Diese kann nun f\u00fcr die Erstellung des Native Image verwendet werden.<\/p>\n\n\n\n<p>In der Praxis w\u00fcrde die Build-Pipeline also zun\u00e4chst die Bytecode-Variante erstellen. Nun k\u00f6nnen alle automatisierten Tests mit dieser Bytecode-Variante ausgef\u00fchrt werden, wobei der Tracing Agent die reflektiven Zugriffe erkennt. Unter der Annahme, dass dabei wirklich jeder Programmpfad ausgef\u00fchrt wurde, kann nun das Native Image in einem weiteren Build-Schritt erzeugt werden. Dies f\u00fchrt direkt zum n\u00e4chsten Punkt: Bei der Arbeit mit der Native-Image-Technologie wird der Build-Prozess insgesamt l\u00e4nger und komplexer.<\/p>\n\n\n\n<p>Zusammenfassend bedeutet dies, dass beim Einsatz der Native-Image-Technologie einige Dinge kaum oder nicht m\u00f6glich sind (bspw. Security Manager). Viele andere Dinge funktionieren zwar grunds\u00e4tzlich, m\u00fcssen aber umst\u00e4ndlich konfiguriert werden. Hierf\u00fcr ist eine Tool-Unterst\u00fctzung gegeben und wird auch recht dynamisch weiterentwickelt. Die Hoffnung ist hier, dass die Mehraufw\u00e4nde (abgesehen von der Build-Dauer) durch die Werkzeuge kompensiert werden k\u00f6nnen. Allerdings wird dadurch der Build-Prozess auch komplexer und somit fehleranf\u00e4lliger.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Fallstricke unter Windows<\/h3>\n\n\n\n<p>Abschlie\u00dfend noch ein Blick zur Windows-Plattform: Diese wird mittlerweile auch unterst\u00fctzt. Vorbereitend f\u00fcr diesen Blogbeitrag wurden die Versionen \u201eGraalVM Enterprise 20.3.0&#8243; sowie &#8222;GraalVM Enterprise 21.0.0.2\u201c auf einem Windows-System betrachtet. Leider war hier die Dokumentation noch etwas l\u00fcckenhaft und das Tooling greift noch nicht so gut ineinander wie in der Linux-Umgebung. Dadurch gab es auch Hindernisse, die unter Linux nicht aufgefallen sind. So trat beispielsweise ein Problem bei der Erstellung eines Native Images auf, wenn der zugrundeliegende Bytecode von einem anderen JDK (in dem Fall durch das OpenJDK) erzeugt wurde. Dabei ist die auftretende Fehlermeldung leider auch nicht so aussagekr\u00e4ftig, dass sie auf die eigentliche Ursache hinweist:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">native-image de.zeiss.zdi.graal.HelloWorld\n\n[de.zeiss.zdi.graal.helloworld:20764]    classlist:     947.02 ms,  0.96 GB\n[de.zeiss.zdi.graal.helloworld:20764]        (cap):   3,629.54 ms,  0.96 GB\n[de.zeiss.zdi.graal.helloworld:20764]        setup:   5,005.98 ms,  0.96 GB\n\nError: Error compiling query code (in C:\\Users\\xyz\\AppData\\Local\\Temp\\SVM-13344835136940746442\\JNIHeaderDirectives.c). Compiler command ''C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\BuildTools\\VC\\Tools\\MSVC\\14.28.29333\\bin\\HostX64\\x64\\cl.exe' \/WX \/W4 \/wd4244 \/wd4245 \/wd4800 \/wd4804 \/wd4214 '-IC:\\Program Files\\Java\\graalvm-ee-java11-21.0.0.2\\include\\win32' '\/FeC:\\Users\\xyz\\AppData\\Local\\Temp\\SVM-13344835136940746442\\JNIHeaderDirectives.exe' 'C:\\Users\\xyz\\AppData\\Local\\Temp\\SVM-13344835136940746442\\JNIHeaderDirectives.c' ' output included error: [JNIHeaderDirectives.c, Microsoft (R) Incremental Linker Version 14.28.29337.0, Copyright (C) Microsoft Corporation.  All rights reserved., , \/out:C:\\Users\\xyz\\AppData\\Local\\Temp\\SVM-13344835136940746442\\JNIHeaderDirectives.exe , JNIHeaderDirectives.obj , LINK : fatal error LNK1104: Datei \"C:\\Users\\xyz\\AppData\\Local\\Temp\\SVM-13344835136940746442\\JNIHeaderDirectives.exe\" kann nicht ge?ffnet werden.]\n\nError: Use -H:+ReportExceptionStackTraces to print stacktrace of underlying exception\nError: Image build request failed with exit status 1<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Ein weiterer Fallstrick bestand in der laufwerks\u00fcbergreifenden Arbeit: Es ist unter Windows leider nicht m\u00f6glich, die GraalVM auf einem Laufwerk zu installieren (in dem Fall unter C:\\Programme) und auf einem anderen Laufwerk auszuf\u00fchren (in dem Fall unter D:\\dev\\prj\\&#8230;):<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">native-image de.zeiss.zdi.graal.HelloWorld\n\n[de.zeiss.zdi.graal.helloworld:10660]    classlist:   3,074.80 ms,  0.96 GB\n[de.zeiss.zdi.graal.helloworld:10660]        setup:     314.93 ms,  0.96 GB\n\nFatal error:java.lang.IllegalArgumentException: java.lang.IllegalArgumentException: 'other' has different root\n        at java.base\/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)\n[\u2026]<\/pre>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Dar\u00fcber hinaus konnten mit dem Native Image in der Windows-Umgebung leider keine Performance-Vorteile festgestellt werden. Derzeit ist die Windows-Unterst\u00fctzung (sowohl die GraalVM-Toolchain selbst als auch die generierten Native Images) somit noch eher experimentell.<\/p>\n\n\n\n<div style=\"height:30px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n\n\n<p>Im Blogbeitrag wurde vor allem darauf eingegangen, dass die Startzeit der Java-Programme mit GraalVMs Native-Image-Technologie enorm verbessert werden kann. Es wurde dargestellt, welchen Ans\u00e4tzen und welcher Techniken sich die GraalVM dabei bedient. Das Resultat wurde mit Messungen an Beispiel-Programmen untermauert. Auf der anderen Seite wurden aber auch einige Herausforderungen genannt, welche beim Einsatz der Native-Image-Technologie auftreten.<\/p>\n\n\n\n<p>Mit l\u00e4nger laufenden Programmen sind vermutlich kaum Performance-Steigerungen zu erwarten, weil dann auch die Optimierungen der herk\u00f6mmlichen JVMs greifen. Das ist erst einmal nur eine Behauptung. Eine Untersuchung dieses Aspekts w\u00fcrde den aktuellen Rahmen sprengen und hat genug Potenzial f\u00fcr einen eigenen Blogbeitrag.<\/p>\n\n\n\n<p>Nun wollen wir uns noch kurz den Fragen aus der Einleitung zuwenden: Grunds\u00e4tzlich ist das \u201eherk\u00f6mmliche\u201c Java schon lange nicht mehr langsam, sondern rasend schnell. Schon allein der Einsatz im (rechenintensiven) Big-Data-Umfeld ist ein Indiz hierf\u00fcr. Die haupts\u00e4chliche Vorbedingung f\u00fcr diese hohe Performance ist eine gewisse Warmlaufzeit. Im Umkehrschluss hei\u00dft das: Der Start eines herk\u00f6mmlichen Java-Programms l\u00e4sst zu w\u00fcnschen \u00fcbrig \u2013 und genau dies ist der Hauptansatzpunkt der Native-Image-Technologie. Auf der anderen Seite bringt diese Technologie auch eine Menge Nachteile mit \u2013 vor allem f\u00fcr gro\u00dfe, technik-reiche Anwendungen.<\/p>\n\n\n\n<p>Zusammenfassend l\u00e4sst sich sagen, dass die GraalVM das Potenzial hat, sich in verschiedenen Einsatzgebieten zu etablieren. Die polyglotten Eigenschaften k\u00f6nnen sich mehrsprachige Anwendungen zunutze machen und der Einsatz der betrachteten Native-Image-Technologie ist vor allem f\u00fcr Cloud Services durchaus denkbar. Allerdings wird sich der Einsatz der GraalVM vor allem f\u00fcr rechenintensive (d. h. meist auch l\u00e4nger laufende) nicht-triviale Anwendungen vermutlich nicht lohnen.<\/p>\n\n\n\n<p>Abschlie\u00dfend soll noch als Vorteil f\u00fcr die GraalVM festgehalten werden, dass Compiler und Optimizer in Java implementiert sind. Das ist zwar zun\u00e4chst grunds\u00e4tzlich nicht besser oder schlechter als die bisherigen Implementierungen \u2013 wird aber mittel- bis langfristig die Chancen erh\u00f6hen, das Potential der Java-Community besser zu nutzen.<\/p>\n\n\n\n<p>Alles in allem bleibt es wohl spannend: Eine grunds\u00e4tzliche Abl\u00f6sung des OpenJDK ist derzeit nicht absehbar. Und man darf schlie\u00dflich auch nicht vergessen, dass die Entwicklung dort genauso wenig stillsteht. Allerdings hat die GraalVM durchaus das Potenzial, um sich (zun\u00e4chst erst einmal?) in bestimmten Anwendungsgebieten zu etablieren.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>GraalVM verspricht bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen. Wie sieht es mit der Performance aus? Warum sollte die GraalVM wesentlich performanter sein? Was macht sie anders als die herk\u00f6mmlichen JDKs?<\/p>\n","protected":false},"author":138,"featured_media":2906,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"advgb_blocks_editor_width":"","advgb_blocks_columns_visual_guide":"","footnotes":""},"categories":[15],"tags":[406,570,647,648,649,673,674,675,218,337],"topics":[],"class_list":["post-2798","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-java","tag-performance","tag-zeiss-digital-innovation","tag-graalvm","tag-interpreter","tag-compiler","tag-jdk","tag-performance-steigerung","tag-jit-compiler","tag-java","tag-editorschoice"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.0 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>GraalVM \u2013 Ein Blick auf die Performance - ZEISS Digital Innovation Blog<\/title>\n<meta name=\"description\" content=\"GraalVM verspricht bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen. Wie sieht es mit der Performance aus?\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"GraalVM \u2013 Ein Blick auf die Performance\" \/>\n<meta property=\"og:description\" content=\"GraalVM verspricht bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen. Wie sieht es mit der Performance aus?\" \/>\n<meta property=\"og:url\" content=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/\" \/>\n<meta property=\"og:site_name\" content=\"Digital Innovation Blog\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/ZEISSDigitalInnovation\/\" \/>\n<meta property=\"article:published_time\" content=\"2022-02-18T10:12:25+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-06-16T12:26:57+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-scaled.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"2560\" \/>\n\t<meta property=\"og:image:height\" content=\"1706\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Kai Zickmann\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"GraalVM \u2013 Ein Blick auf die Performance\" \/>\n<meta name=\"twitter:description\" content=\"GraalVM verspricht bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen. Wie sieht es mit der Performance aus?\" \/>\n<meta name=\"twitter:creator\" content=\"@ZEISS_di\" \/>\n<meta name=\"twitter:site\" content=\"@ZEISS_di\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Kai Zickmann\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"25\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/\",\"name\":\"GraalVM \u2013 Ein Blick auf die Performance - ZEISS Digital Innovation Blog\",\"isPartOf\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-scaled.jpg\",\"datePublished\":\"2022-02-18T10:12:25+00:00\",\"dateModified\":\"2022-06-16T12:26:57+00:00\",\"author\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#\/schema\/person\/706084b65d4f0555d5fb2897cbf455fe\"},\"description\":\"GraalVM verspricht bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen. Wie sieht es mit der Performance aus?\",\"breadcrumb\":{\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#primaryimage\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-scaled.jpg\",\"contentUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-scaled.jpg\",\"width\":2560,\"height\":1706},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"GraalVM \u2013 Ein Blick auf die Performance\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#website\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/\",\"name\":\"Digital Innovation Blog\",\"description\":\"\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#\/schema\/person\/706084b65d4f0555d5fb2897cbf455fe\",\"name\":\"Kai Zickmann\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2025\/02\/2024-08-06-JG-336_1000x-1000-1-150x150.jpg\",\"contentUrl\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2025\/02\/2024-08-06-JG-336_1000x-1000-1-150x150.jpg\",\"caption\":\"Kai Zickmann\"},\"description\":\"Kai Zickmann studierte Informatik an der TU Dresden und ist seit 2011 bei ZEISS Digital Innovation als Senior Software Developer t\u00e4tig. Seine beruflichen Schwerpunkte liegen auf den Themen Architektur, Implementierung, Test, Build und CI sowie auf ausgew\u00e4hlten Neuerungen im Bereich Java.\",\"url\":\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/author\/kaizickmann\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"GraalVM \u2013 Ein Blick auf die Performance - ZEISS Digital Innovation Blog","description":"GraalVM verspricht bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen. Wie sieht es mit der Performance aus?","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/","og_locale":"de_DE","og_type":"article","og_title":"GraalVM \u2013 Ein Blick auf die Performance","og_description":"GraalVM verspricht bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen. Wie sieht es mit der Performance aus?","og_url":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/","og_site_name":"Digital Innovation Blog","article_publisher":"https:\/\/www.facebook.com\/ZEISSDigitalInnovation\/","article_published_time":"2022-02-18T10:12:25+00:00","article_modified_time":"2022-06-16T12:26:57+00:00","og_image":[{"width":2560,"height":1706,"url":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-scaled.jpg","type":"image\/jpeg"}],"author":"Kai Zickmann","twitter_card":"summary_large_image","twitter_title":"GraalVM \u2013 Ein Blick auf die Performance","twitter_description":"GraalVM verspricht bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen. Wie sieht es mit der Performance aus?","twitter_creator":"@ZEISS_di","twitter_site":"@ZEISS_di","twitter_misc":{"Verfasst von":"Kai Zickmann","Gesch\u00e4tzte Lesezeit":"25\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/","name":"GraalVM \u2013 Ein Blick auf die Performance - ZEISS Digital Innovation Blog","isPartOf":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#primaryimage"},"image":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#primaryimage"},"thumbnailUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-scaled.jpg","datePublished":"2022-02-18T10:12:25+00:00","dateModified":"2022-06-16T12:26:57+00:00","author":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#\/schema\/person\/706084b65d4f0555d5fb2897cbf455fe"},"description":"GraalVM verspricht bessere Laufzeiteigenschaften und die Integration mehrerer Programmiersprachen. Wie sieht es mit der Performance aus?","breadcrumb":{"@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#primaryimage","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-scaled.jpg","contentUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-scaled.jpg","width":2560,"height":1706},{"@type":"BreadcrumbList","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/graalvm-ein-blick-auf-die-performance\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/"},{"@type":"ListItem","position":2,"name":"GraalVM \u2013 Ein Blick auf die Performance"}]},{"@type":"WebSite","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#website","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/","name":"Digital Innovation Blog","description":"","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Person","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#\/schema\/person\/706084b65d4f0555d5fb2897cbf455fe","name":"Kai Zickmann","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/#\/schema\/person\/image\/","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2025\/02\/2024-08-06-JG-336_1000x-1000-1-150x150.jpg","contentUrl":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2025\/02\/2024-08-06-JG-336_1000x-1000-1-150x150.jpg","caption":"Kai Zickmann"},"description":"Kai Zickmann studierte Informatik an der TU Dresden und ist seit 2011 bei ZEISS Digital Innovation als Senior Software Developer t\u00e4tig. Seine beruflichen Schwerpunkte liegen auf den Themen Architektur, Implementierung, Test, Build und CI sowie auf ausgew\u00e4hlten Neuerungen im Bereich Java.","url":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/author\/kaizickmann\/"}]}},"author_meta":{"display_name":"Kai Zickmann","author_link":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/author\/kaizickmann\/"},"featured_img":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-content\/uploads\/sites\/2\/2022\/02\/3-600x400.jpg","coauthors":[],"tax_additional":{"categories":{"linked":["<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">Java<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">Java<\/span>"]},"tags":{"linked":["<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">Performance<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">ZEISS Digital Innovation<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">GraalVM<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">Interpreter<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">Compiler<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">JDK<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">Performance-Steigerung<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">JIT-Compiler<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">Java<\/a>","<a href=\"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/category\/java\/\" class=\"advgb-post-tax-term\">Editor&#039;s Choice<\/a>"],"unlinked":["<span class=\"advgb-post-tax-term\">Performance<\/span>","<span class=\"advgb-post-tax-term\">ZEISS Digital Innovation<\/span>","<span class=\"advgb-post-tax-term\">GraalVM<\/span>","<span class=\"advgb-post-tax-term\">Interpreter<\/span>","<span class=\"advgb-post-tax-term\">Compiler<\/span>","<span class=\"advgb-post-tax-term\">JDK<\/span>","<span class=\"advgb-post-tax-term\">Performance-Steigerung<\/span>","<span class=\"advgb-post-tax-term\">JIT-Compiler<\/span>","<span class=\"advgb-post-tax-term\">Java<\/span>","<span class=\"advgb-post-tax-term\">Editor&#039;s Choice<\/span>"]}},"comment_count":"0","relative_dates":{"created":"Posted 4\u00a0Jahren ago","modified":"Updated 4\u00a0Jahren ago"},"absolute_dates":{"created":"Posted on Februar 18, 2022","modified":"Updated on Juni 16, 2022"},"absolute_dates_time":{"created":"Posted on Februar 18, 2022 10:12 a.m.","modified":"Updated on Juni 16, 2022 12:26 p.m."},"featured_img_caption":"","series_order":"","_links":{"self":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/posts\/2798","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/users\/138"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/comments?post=2798"}],"version-history":[{"count":16,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/posts\/2798\/revisions"}],"predecessor-version":[{"id":3021,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/posts\/2798\/revisions\/3021"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/media\/2906"}],"wp:attachment":[{"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/media?parent=2798"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/categories?post=2798"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/tags?post=2798"},{"taxonomy":"topics","embeddable":true,"href":"https:\/\/blogs.zeiss.com\/digital-innovation\/de\/wp-json\/wp\/v2\/topics?post=2798"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}