Graal(VM) – Versuch eines Überblicks

Die Themen Graal bzw. GraalVM beschäftigen mich schon eine ganze Weile, und wie ich erneut auf dem Java Forum Stuttgart 2019 bemerkt habe, hat das Graal-Universum derart viele Facetten, die sich zudem alle in einem unterschiedlichen Stand der Nützlichkeit und Nutzbarkeit befinden, so dass ich hier mal versuchen werde, einen Überblick zu geben.

Das Graal-Universum (auch gerne “GraalVM ecosystem” genannt) ist ein Oracle-Projekt, hervorgegangen aus zahlreichen Forschungsprojekten der Oracle Labs in Kooperation mit der Uni Linz – früher hieß das Projekt “Metropolis” (und noch früher Maxine VM) und hatte ursprünglich neben Forschung angeblich nur das Ziel, einen JIT-Compiler für die Hotspot-JVM in Java neu zu schreiben, weil vor allem der in C++ geschriebene “C2” (auch als “HotSpot-Compiler der Server-JVM” bekannt) das Ende seiner Erweiterbarkeit und Wartbarkeit erreicht hatte. Inzwischen ist sehr viel mehr daraus geworden.

Übrigens steht “Graal” für “General Recursive Applicative and Algorithmic Language”. Was ein Glück, dass niemand auf die daraus eigentlich resultierende Schreibweise “GRAAL” besteht. Ein weiterer wichtiger Hinweis: das Wording wurde über die Zeit verändert, bitte nicht verwirren lassen von Graal vs. GraalVM vs. Graal-Compiler vs. Graal-JIT. Derzeit scheint man sich auf “GraalVM” für das Gesamtpaket entschieden zu haben, mit “Graal” als Kurzform, wenn man drüber spricht.

Die (JVM-)Welt vor GraalVM

Viel wurde geschrieben zur JVM, zu Bytecode, zu Interpretern, zu JIT-Compilern und der dynamischen Recompilierung à la HotSpot. Vor GraalVM war die HotSpot-JVM von Sun/Oracle die übliche Laufzeitumgebung für Bytecode aller Art (Dinge wie Excelsior JET, IBM/Eclipse J9, Azul Zing oder sogar gcj aus früheren Zeiten waren und sind ja eher Exoten). Die HotSpot-JVM hat im Prinzip drei Möglichkeiten, um Bytecode zu behandeln: entweder er wird interpretiert, oder er wird mit dem C1-Compiler (auch “client compiler” genannt) in Maschinencode übersetzt (das geht schnell, ist aber wenig optimierter Code), oder er wird mit dem C2-Compiler (auch “server compiler” genannt) in Maschinencode übersetzt (das dauert etwas länger, ist aber sehr gut optimiert, und kann zur Laufzeit, wenn eine Methode beispielsweise sehr häufig aufgerufen wird, weiter optimiert werden – das ist der “HotSpot”-Aspekt der JVM). Das Zusammenspiel aus C1-C2 wird oft auch “tiered compilation” genannt.

Nun ist das C++-Duo C1-C2 schon gut 20 Jahre alt und nach verschiedenen Aussagen diverser Beteiligter am Ende von Weiterentwicklung und Wartbarkeit angekommen. C2 wurde in den letzten Jahren nur noch mit spitzen Fingern angefasst, es wurden keinerlei Verbesserungen an der Architektur oder der prinzipiellen Optimierungsmöglichkeiten vorgenommen, nur Bugfixing und diverse Optimierungen im Bereich Intrinsics (also das Erkennen spezifischer Codepatterns und Verwendung von optimiertem Maschinencode um diese direkt zu ersetzen), was aber als Backend-Optimierung für jede unterstützte Prozessorarchitektur separat gemacht werden musste (oder der Einfachheit halber gleich unterblieb).

Man könnte argumentieren, dass das (also Stabilität und wenige Änderungen) auch gut so ist: durch die Ausführung als native code in der JVM besteht schließlich immer die Gefahr, bei einem unerwarteten Fehler die komplette JVM mit einem Coredump nach einem Segmentation Fault herunterzureißen. Und schließlich muss man ja zugeben, dass der C2 insbesondere Java-Bytecode in wirklich effizienten Maschinencode überführt, der für viele Anwendungen den Performancevergleich mit statisch compilierten Sprachen wie C oder C++ nicht zu scheuen braucht, und teilweise aufgrund seiner dynamischen Natur auf Basis von Informationen, die erst zur Laufzeit zur Verfügung stehen, diese sogar performancetechnisch übertreffen kann.

Diese uns allen bekannte JVM-Welt hat aber natürlich auch bekannte Schwächen, so dass das Festhalten am Status Quo kaum als eine valide Vorgehensweise für die Zukunft erscheint. Da ist zum einen der nicht zu vernachlässigende Performancenachteil beim Startup einer Anwendung – es dauert seine Zeit, bis die entscheidenden “heißen” Codepfade durch C2 entsprechend optimiert wurden, ein Prozess, der oft als “heating up the JVM” oder ähnlich bezeichnet wird. Und während dieser Zeit genehmigt sich die JVM ja durchaus auch erkleckliche Mengen an Speicher, und der Garbage Collector darf regelmäßig aufräumen. So schön das Prinzip einer virtuellen Maschine auch sein mag, in gewissen Anwendungsszenarien wie z.B. Desktop-Anwendungen oder Cloud-Services sind die Nachteile unübersehbar.

Dazu kommt, dass sich mit der Zeit herausgestellt hat, dass die Techniken des C2 zu nah an Java und der Art und Weise, wie javac Bytecode erzeugt, gebaut sind. Das führt zu suboptimalen Ergebnissen bei der Ausführung von Bytecode aus anderen Quellen, sei es Scala, JRuby, Jython oder Clojure.

Und dann ist da natürlich das Problem des “Systembruchs” bei der Weiterentwicklung des JITs – man muss immer zwei Welten verstehen, entwickeln, analysieren und debuggen – die native C++-Welt und die Java-Welt. Qualifiziertes Personal, das in beiden Welten zuhause ist, ist naturgemäß schwer zu finden.

Jedenfalls kann man nicht wegdiskutieren, dass das Großthema Performance in den Teilbereichen “startup” und “footprint” mit der bisherigen Strategie kaum ausreichend gelöst werden kann, vor allem wenn im Serverbereich nicht mehr länger große, langlaufende Prozesse das übliche Architekturmuster sind, sondern stattdessen kurzlaufende, containerisierte, über ihre schiere Anzahl skalierende Microservices. Bisher wurden diese Themen nur im Rahmen des gewohnten Umfelds angegangen – Class Sharing, Modularisierung des JDK, AOT seit Java 9. Mit begrenztem Erfolg.

Graal der JIT-Compiler aka “GraalVM compiler”

Angeblich beginnt die Geschichte von Graal(VM) etwa 2009 mit einer Portierung von C1/C2 nach Java durch Thomas Würthinger. Heute kann man den Graal-JIT-Compiler recht einfach in JDK 8 oder JDK 11 integrieren, kann also von jedem Java-Entwickler sehr einfach ausprobiert werden. So kann man in Ruhe für seinen spezifischen Workload herausfinden, ob die fortgeschrittenen Optimierungstechniken tatsächlich einen Laufzeitvorteil bedeuten, und wie groß dieser gegebenenfalls ausfällt.

Vor geraumer Zeit (Java 8, zunächst aber nicht öffentlich dokumentiert, offiziell dabei seit Java 11) wurde das JVMCI (JVM Compiler Interface, JEP 243) eingeführt, eine (hoffentlich langzeit-)stabile Schnittstelle der JVM zum Andocken alternativer JIT-Compiler. Graal kann genau diese Schnittstelle nutzen.

Wo bietet Graal nun Vorteile gegenüber dem bisherigen C2? Insbesondere die sogenannte “Escape Analysis” scheint sehr viel leistungsfähiger zu sein. D.h. der Compiler ist viel besser beim Erkennen von Objekten, deren Gültigkeitszeit z.B. auf Methodenebene beschränkt ist und deshalb problemlos auf dem Stack statt auf dem Heap allokiert werden kann. Das hat doppelten Nutzen, denn die Stack-Allokation ist schneller und der GC muss hinterher nicht aufräumen. Besonders bei Scala-Code scheint diese Verbesserung für erhebliche Performanceverbesserungen zu sorgen. Aber auch das Inlining von Code soll mit Graal besser funktionieren.

Die Tatsache, dass Graal in Java geschrieben ist, hat interessante Konsequenzen. Zum einen wird nun der JIT-Compiler selbst in der JVM ausgeführt und optimiert sich so mit der Zeit selbst. Speicherallokation für den Compiler findet auf dem JVM-Heap statt anstelle aus dem Native-Pool zu kommen. Da nun auch die Performance des Compilers einer Warm-Up-Zeit unterliegt, verändert sich auch das Performance-Profil zur Ausführungszeit. Und der GC ist natürlich potenziell stärker beschäftigt, weil er sich nun sowohl mit der eigentlichen Anwendung als auch mit dem Compiler beschäftigen muss.

Um dieses Szenario zu nutzen, kann man einfach die GraalVM oder einen der OpenJDK-Builds vom GraalVM-Team nutzen und entsprechend konfigurieren. Im Prinzip ein “drop-in replacement” für das gewohnte JDK.

Und was ist libgraal?

libgraal ist der Versuch, den Graal-JIT-Compiler mittels GraalVM-native-image (dazu später mehr) in eine Bibliothek zu überführen, die das Performance-Problem des Compile-Vorgangs des Graal-JIT-Compilers in der JVM lösen soll. Da schon alles in nativem Code vorliegt, muss sich der JIT-Compiler nur noch um den Anwendungscode kümmern und nicht mehr um sich selbst. Im Prinzip wird dadurch Graal zu einem – was das Laufzeitverhalten angeht – sehr ähnlichen JIT-Compiler wie der klassische C1 und C2 in der HotSpot-JVM. Langsame JIT-Compilierung während der JVM-Warm-Up-Phase wird dadurch verhindert.

Die aktuellen Versionen von GraalVM sowie die GraalVM-OpenJDK-Builds enthalten allesamt libgraal out-of-the-box.

GraalVM und die AOT-Compilierung

Seit Java 9 gibt es im JDK ein als experimentell markiertes Tool namens jaotc, das (eine recht frühe Version von) Graal als Backend verwendet. Die Implementierung erfolgte im Rahmen von JEP 295, dort kann man viele der Einschränkungen nachlesen. Im Prinzip gibt man jaotc vor, welcher Code vorcompiliert werden soll, und es wird ein Binär-Blob erzeugt, der dann den sonst durch den JIT-Compiler erzeugten Maschinencode enthält. Man spart also die Zeit, die der JIT-Compiler sonst zur Laufzeit braucht, um den Code zu compilieren.

Es gibt bei jaotc jede Menge Einschränkungen, die den Einsatz in der Praxis wirkungsvoll verhindert haben – zumindest in der Breite. Immerhin gab es bei Java 10 einige Bemühungen, um diverse gravierende Einschränkungen wie “funktioniert nur unter Linux-x64” aufzuheben. GraalVM geht heute aber noch einen großen Schritt weiter mit “native-image”.

GraalVM und das “Native Image”-Feature

Um Memory-Footprint und Startup-Zeit zu minimieren, unterstützt GraalVM die Möglichkeit, ein “Native Image” zu erzeugen. Simpel gesagt: ein standalone ausführbares Programm, das keine separate JVM benötigt. Die bisherigen Releaseversionen von GraalVM inklusive der derzeit aktuellen 19.1.0 erlauben die Erstellung eines solchen “Native Images” für Linux und macOS. Dazu wird neben der GraalVM-Distribution auch noch eine native Toolchain für das Betriebssystem benötigt, für Linux beispielsweise gcc nebst Bibliotheken wie glibc und zlib. Für die noch sehr experimentelle Windows-Unterstützung wird eine Installation von “Windows SDK for Windows 7” benötigt.

Es gibt ein paar Dinge neu zu durchdenken, wenn man ein “Native Image” erzeugt. Beispielsweise die Frage, was eigentlich schon zur Compilezeit aufgelöst wird und was erst zur Laufzeit. Stichwort “Initialisierung statischer Member”. Man überlege sich, inwieweit sich die Semantik des Programms ändert, wenn in einem static initializer eine Variable mit “new Date()” belegt wird und dies einmal zur Compilezeit passiert. Für solche Fälle werden Compile-Time-Switches angeboten.

Prinzipbedingt gibt es einige Limitierungen beim native-image-Feature. Wichtig ist die “closed-world assumption”. GraalVM muss bei der statischen Compilierung schließlich entscheiden können, welche Klassen für das Image eingebunden werden müssen. Das schließt übliche Java-Mittel wie dynamisches Nachladen von Klassen zur Laufzeit aus, deren Notwendigkeit auch erst zur Laufzeit auftaucht – typische Service-Lookup-Konstruktionen, beispielsweise im OSGi-Umfeld, gehören zu diesem Problemkreis. Ein Feature, um über einen Tracing Agent auch zur Laufzeit dynamisch nachgeladene Klassen sicher zu identifizieren, befindet sich derzeit in der Entwicklung. Aber auch das würde natürlich vollständig dynamisches Nachladen nur dann erfassen, wenn alle entscheidenden Codepfade auch tatsächlich durchlaufen werden, während der Tracing Agent aktiv ist.

Erstaunlicherweise ist trotzdem partieller Support für Reflection verfügbar, was uns zum nächsten Teilthema führt…

GraalVM und die Substrate VM

Auch nativer Code braucht natürlich, wenn die Semantik eines Java-Programms möglichst unangetastet bleiben soll, eine gewisse Laufzeitumgebung. Die wird im Falle eines GraalVM-erzeugten “Native Image” durch die eingebettete Substrate VM zur Verfügung gestellt. Dazu gehören Runtime-Routinen und der Garbage Collector. Die Komponenten der Substrate VM sind in Java geschrieben.

GraalVM und Truffle

Bisher waren die Punkte sehr Java-spezifisch, oder genauer gesagt Bytecode-spezifisch – alles problemlos nutzbar für das JVM-Bytecode-Ökosystem, also neben Java auch Kotlin, Scala, Groovy, Clojure und Konsorten. So weit, so unspektakulär. Andere Programmiersprachen haben häufig Bytecode-spezifische Forks als Implementierung wie Jython oder JRuby, und das JDK hatte bekanntlich seinen eigenen JavaScript-Interpreter intus namens Rhino (ab Java 6) und Nashorn (ab Java 8).

Aber GraalVM bietet mehr. Das Truffle-Framework erlaubt die einfache Implementierung von Interpretern, die dann vollautomatisch zu einem Bestandteil der GraalVM werden können – quasi eine automatische Generierung eines Compilers aus einem Interpreter.

Interpreter für Sprachen wie JavaScript, Ruby, R und Python sind bereits heute in verschiedenen Stadien der Fertigstellung bereit zu Experimenten. Die JavaScript-Implementierung scheint am weitesten fortgeschritten und umfasst bereits den Sprachumfang von ECMAScript 2019. Das war übrigens das Hauptproblem mit den Java-internen JavaScript-Lösungen – sie konnten mit der schnellen Weiterentwicklung von ECMAScript nicht mithalten und waren schnell veraltet und schwer wartbar.

Eine gute Zusammenfassung in wenigen Worten: “Truffle makes it easy, Graal makes it fast.” Wer schon mal versucht hat, für eine einfache Sprache einen Interpreter zu entwickeln, weiß, dass das durchaus machbar ist. Ein optimierter Interpreter ist schon schwieriger. Aber einen gescheit optimierenden Compiler dafür zu schreibe, ist sauschwer. Soll der auch noch mit in anderen Sprachen geschriebenem Code kommunizieren, wird es langsam wirklich komplex. Das GraalVM-Ökosystem verspricht hier eine einfach handhabbare und trotzdem sehr leistungsfähige und performante Lösung.

GraalVM als polyglotte VM

Nun wäre es ja schon ein echter Fortschritt, die GraalVM als universelle Laufzeitumgebung für allerlei Sprachen zur Verfügung zu haben. Beispielsweise könnte man sich für die gängigen Interpreter-Sprachen wie eben Ruby oder Python oder auch Lua die zeitaufwändige Entwicklung einer effizienten Laufzeitumgebung schlicht sparen – warum immer wieder neu einen JIT-Compiler implementieren, wenn es in GraalVM schon einen gibt? Aber wenn das nun schon möglich ist, wie wäre es denn dann, wenn man die Sprachen beliebig mischen könnte?

Das wäre natürlich phantastisch, und GraalVM bietet genau das. Es existieren Schnittstellen, um die Sprachen kontrolliert miteinander verbinden zu können. Und zwar, weil alle in demselben VM-Kontext laufen, quasi ohne zusätzlichen Overhead. So werden Java-Objekte für JavaScript-Skripte zugreifbar, Methoden aufrufbar, das volle Programm. Die technische Magie dazu steckt im “polyglot interoperability protocol” des Truffle-Frameworks, das genau diese Dinge standardisiert und damit diese Interoperabilität automatisch sicherstellt.

Der alte Traum von pick-and-mix bezüglich unterschiedlicher Programmiersprachen mit ihren spezifischen Stärken und leistungsfähigen Bibliotheken, hier wird er Wirklichkeit.

Wirklich cool ist beispielsweise eine für GraalVM erweiterte Variante von VisualVM. Man kann damit den laufenden Code in Ruby, R oder JavaScript genau untersuchen hinsichtlich der Threads und der Objekte auf dem Heap.

Und was ist Sulong?

Sulong ist eine weitere Schicht der GraalVM-Welt – ein High-Performance-Interpreter für LLVM Bitcode. Das erlaubt es der GraalVM, Programmcode, der in diversen Sprachen auf Basis der LLVM-Compiler-Infrastruktur erzeugt wird (besonders prominent: C, C++, Objective-C, Swift und Rust), ebenfalls auszuführen. Mit voller Interoperabilität mit allen anderen unterstützten Sprachen des GraalVM-Universums.

Ich erkenne hier eine leichte Ironie, schließlich hat Azul Systems für ihre Zing JVM einen neuen JIT-Compiler namens “Falcon” entwickelt, der auf der LLVM-Compiler-Infrastruktur basiert. Quasi genau andersrum.

Community Edition vs. Enterprise Edition

Oracle ist nicht gerade bekannt dafür, Projekte aus reiner Menschenfreundlichkeit kostenlos unter die Leute zu bringen. Demzufolge strebt man auch im Graal-Universum einen RoI an, und der heißt “GraalVM Enterprise Edition”. Es gibt einige Optimierungen, die nur in der Enterprise-Variante zur Verfügung stehen, Stand heute scheint das vor allem die Autovektorisierung zu sein. Man kann die Enterprise-Variante jederzeit zu Evaluierungszwecken herunterladen und so prüfen, ob das beim eigenen Workload etwas bringt – die Evaluierungsversion darf jedoch nicht in Produktion verwendet werden.

Ob und wann Optimierungen von der Enterprise-Version in die Community-Version wandern, muss abgewartet werden. Oracle wird sicherlich anstreben, immer einen Respektabstand zwischen den beiden Versionen zu wahren und vor allem Optimierungen für die Großen der Branche möglicherweise exklusiv für die Enterprise Edition reservieren. Letztlich muss der Performance-Gewinn durch die Enterprise-Edition ja im realen Leben gegen eingesparte Kosten z.B. im Cloud-Umfeld gerechnet werden – der Business-Case muss möglichst leicht nachvollziehbar sein, wenn man Lizenzen verkaufen will.

Bezüglich der Lizenzsituation bleibt festzuhalten, dass praktisch alle GraalVM-Komponenten unter GPL2 mit Classpath-Exception stehen.

Fazit, Status Quo, Ausblick, Glaskugel

Das Potenzial ist gewaltig – viele Schwächen des Java-Ökosystems können mit GraalVM gelöst oder zumindest gemildert werden. Dazu kommt mit der Idee der polyglotten VM der alte Traum der völligen Austauschbarkeit von Programmiersprachen und Bibliotheken in greifbare Nähe. Mit der “Native Image”-Möglichkeit wird Java auch wieder (oder zum ersten Mal?) eine sehr gute Lösung für klassische Apps und Anwendungen neben den unbestreitbaren Vorteilen für servserseitig kurzlaufende Services in einem containerisierten Cloudumfeld.

Was hält uns davon ab, genau jetzt vollständig in die GraalVM-Welt einzutauchen? Aus meiner Sicht ist das GraalVM-Ökosystem im Moment in einem uneinheitlichen Zustand. Während das Szenario “GraalVM als JIT-Plugin für die HotSpot-JVM” stabil ist, ist der Rest eher von wechselhafter Stabilität. Vor allem das Feature “native image” scheint im Moment eher limitiert und zurecht als “Early Adopter technology” gekennzeichnet, nicht zuletzt durch die Beschränkung auf Linux und macOS – Windows ist für diesen Anwendungsfall als “experimental” markiert. Wenn man sich die GitHub-Issues zu diesem Thema durchliest, wird klar, dass da noch eine Menge Feinarbeit zu leisten ist. AOT hingegen scheint weitgehend stabil zu sein, ist es doch seit Java 9 Teil des JDK.

Was die Polyglot-Fähigkeiten angeht, scheint die JavaScript-Implementierung recht stabil und zudem vollständig zu sein – Node.js ist ein sehr wichtiges Zielszenario, und man braucht durch die Abkündigung der Nashorn-Engine in Java 11 auch beizeiten einen Ersatz dafür. Bei Ruby, R, Python und LLVM-Bitcode steht eher noch ein Fragezeichen bezüglich Vollständigkeit, Performance und Stabilität. Vom technischen Standpunkt aus ist eine Out-of-the-box-Lauffähigkeit von realen Python-Programmen natürlich auch deutlich schwerer zu bewerkstelligen als bei JavaScript, weil das Python-Ökosystem bei vielgenutzten Bibliotheken voller native code zwecks besserer Performance steckt. NumPy und SciPy seien hier als Beispiele genannt. Bei R ist es ähnlich, wo die performance-relevanten Berechnungen oft in nativem Fortran-basierten Code passiert.

Angesichts der kürzlich erfolgten Abkündigung von Nashorn, der JavaScript-Engine im JDK, dürfte eine stabile und performante JavaScript-Fähigkeit ganz oben auf der Prioritätenliste zu sein. Außerdem scheint GraalVM als Ausführungsplattform für das Node.js-Universum auch attraktiv zu sein, vor allem natürlich wegen der einfachen und performanten Interoperabilität zwischen Java und JavaScript.

Die Native-Image-Fähigkeit ist wie gemacht für Microservices, und so gibt es bereits drei seriöse Frameworks, die GraalVM zu diesem Zweck nutzen: Quarkus, Micronaut und Helidon. Die Benchmarks bezüglich Memory-Footprint und Startup-Zeiten sehen schon beeindruckend aus.

Sehr interessant ist die Möglichkeit, die Native-Image-Fähigkeit für Plattformen zu verwenden, wo keine virtuelle Maschine zulässig ist – OK, “Plattformen” ist hier meines Wissens zu Unrecht die Mehrzahl, denn ich kenne nur iOS, das unter dieser willkürlichen Apple-Entscheidung zu leiden hat. Hier ein Artikel, der die Möglichkeiten beleuchtet. Und zurecht anmerkt, dass es schon viele vergebliche Versuche gab, iOS für das Java-Ökosystem einfach zugänglich zu machen.

Nicht zuletzt wäre die Native-Image-Fähigkeit für die wenigen verbliebenen Java-Entwickler, die Desktop-Anwendungen schreiben, eine schöne Sache. Eine einfache exe hat bei der Distribution von Software einfach Vorteile gegenüber dem Bundling einer kompletten Java-Runtime-Umgebung, möglicherweise nebst Einsatz von Obfuscator und exe-Wrapper.

Ein weiterer hier nicht weiter beleuchteter Anwendungszweck ist das Embedden von GraalVM-Technologie in andere Plattformen. Es werden die Oracle-Datenbank und MySQL derzeit genannt, mit diesem Anwendungsfall habe ich mich nicht näher beschäftigt.

Die Entwicklung schreitet im Moment zügig voran – wer am Ball bleiben will, sollte regelmäßig die Release Notes für neue Versionen durchlesen, man bekommt dann ein ganz gutes Gefühl für die Reife von manchem Szenario.

In der optimal vergraalten Welt der Zukunft wäre es ein Leichtes, mit dem Truffle-Framework seine persönliche Lieblingssprache in verhältnismäßig kurzer Zeit aus dem Boden zu stampfen und sofort eine komplette Toolchain inklusive hochoptimierender VM und nativen Code erzeugendem Compiler zur Verfügung zu haben. Inklusive voller Interoperabilität mit den Ökosystemen aller relevanten Programmiersprachen von Java über C++ bis Python. Die Grundbausteine dafür sind vorhanden und durchaus schon von hoher Qualität, aber es fehlt noch reichlich Feinschliff. Ich fürchte aber, dass die Lösung des Gesamtproblems der 80-20-Regel folgt. Wenn nicht gar 90-10.

Aber es wäre in einem ausgereiften Endzustand schon nahe am heiligen Gra(a)l. Ich hoffe, man verzettelt sich nicht in der Vielfalt der Möglichkeiten. Jedenfalls wäre Manpower in der GraalVM-Ecke m.E. deutlich besser aufgehoben als bei mancher heißdiskutierter Spracherweiterung seit Java 9. Ich sage mal “Text Blocks”. “Type Inference”. Private Methoden in Interfaces. Oder JDK-Erweiterungen wie jshell oder Epsilon GC, deren überragende Nützlichkeit sich auf den ersten und auch zweiten Blick nicht direkt erschließt.

Andere Quellen

Erster Anlaufpunkt ist natürlich die GraalVM-Homepage. Sehr empfehlenswert sind YouTube-Videos von Thomas Würthinger und Christian Thalinger (als früherer Entwickler des C2 natürlich spezialisiert auf den GraalVM-JIT aka GraalVM compiler, der früher verwirrenderweise Graal JIT oder Graal compiler hieß).

Wer ganz tief einsteigen will – die Sourcen werden auf GitHub gehostet.

Anlass für diesen Blog-Eintrag war ein Übersichtsartikel auf Heise Developer, der es doch etwas an Tiefe vermissen lies und durch Vermischung der unterschiedlichen Aspekte beim Uneingeweihten möglicherweise für Verwirrung sorgt. Ich überlasse es dem geneigten Leser zu entscheiden, ob mein Artikel eventuell noch wirrer und verwirrender ist.

Java Forum Stuttgart 2019

Über meine Gründe, das Java Forum Stuttgart zu besuchen, habe ich bereits 2017 was geschrieben, und daran hat sich nichts geändert. 2018 habe ich aufgrund wenig vielversprechender Themenauswahl mal sausen lassen, also war es 2019 mal wieder an der Zeit.

Die Rahmenbedingungen waren dieselben: Liederhalle, top organisiert, Verpflegung OK (auch wenn ich wohl nie verstehen werde, warum jemand Coke Light anbietet statt Coke Zero, aber über Geschmack kann man nicht streiten – vielleicht war es ja wenigstens billiger), abwechslungsreiches Vortragsprogramm. Und ein Code of Conduct scheint heutzutage wohl unvermeidlich, natürlich in gendergerechter Sprache anstatt in richtigem Deutsch. Man könnte den ganzen Sermon inhaltlich auch schneller auf den Punkt bringen: “Es gelten die Regeln der westlichen Zivilisation und des allgemeinen Anstands”. Oder “Verhalte Dich stets so, dass Deine Mutter auf Dich stolz wäre.”

Zum eigentlichen Thema. Gab es diesmal einen der typischen Themenschwerpunkte, den klassischen Hype, auf den sich alle stürzen und zu dem es zig Vorträge gibt? Immerhin vier Mal war Graal(VM) (Teil-)Thema, was m.E. zeigt, dass das Thema “Performance” insbesondere im Bereich Startup-Zeiten und Speicherverbrauch in der Java-Welt wie schon vor fast 25 Jahren topaktuell bleibt. Ansonsten scheint jeder – auch ohne Vorliegen zwingender Gründe – sein Heil in der Microservice-Welt – gerne auch in der Cloud – zu suchen. Der Mobile- und IoT-Hype schein hingegen etwas abgeflacht zu sein.

Zum Vortrag “Cross-Platform Mobile-Apps mit Java – 2019” von Dr. Daniel Thommes, Geschäftsführer der NeverNull GmbH. Ein Technologieüberblick, durchaus ins Detail gehend, zur ewig aktuellen Frage “write once, run anywhere, und bitte plattformübergreifend single source und an jede Plattform optimal angepasst”. Daran ist bekanntlich schon Swing gescheitert. Und die Zeichen stehen ehrlich gesagt nicht besonders gut, dass es für so divergierende Anforderungen wie “App auf iOS, App auf Android, und Desktop auf Windows und Linux” jemals eine einheitliche und schöne Lösung geben könnte. Aber der Stand der Dinge hat mich schon etwas erschreckt, denn die “Lösungen” wie Transpiler, portierte Runtimes oder halbgare eigene UI-Toolkits sind doch eher ein Armutszeugnis. Es war ein Non-Sponsored-Talk, was den Vortragenden erkennbar in Gewissensprobleme stürzte und er den Eindruck, die eigene Lösung “MobileUI” seiner Firma zu sehr in den Vordergrund zu stellen, gewissenhaft zu vermeiden suchte. Tut so einem Vortrag nicht gut. Wobei man die Basics durchaus gut beherrscht, wie die “Java Forum Stuttgart”-App – natürlich auf relativ niedrigem Komplexitätsniveau, aber eine Lösung, die die einfachen Sachen sehr gut und sicher beherrscht ist ja auch wertvoll – zeigt. Ebenfalls fehlte erkennbar Tiefe für die diversen altbekannten Fallstricke der Cross-Plattform-UI-Entwicklung. Wenn etwas leidlich Komplexes in Demos nicht angeschnitten wird, liegt das erfahrungsgemäß daran, dass es nicht anständig funktioniert. Aber das ist natürlich nur wilde Spekulation kraft meiner 20-jährigen Erfahrung mit UI-Toolkits aller Art.

Michael Wiedeking war – fast selbstverständlich – auch wieder dabei, diesmal mit “Der eilige Graal” mit einer gewohnt locker präsentierten Reise durch die wunderbare Welt von JIT-Compilern, Hotspots, Bytecode und natürlich Graal. Der Vortrag krankte etwas an der unscharfen Abgrenzung zwischen Graal, GraalVM, Graal-JIT, Graal AOT und Graal native image-Feature. Für den Uneingeweihten ging es da manchmal zu sehr durcheinander, dank ausreichender Vorbildung konnte ich jedoch folgen. Aber selbst die Graal-Erfinder selbst tun sich da oft schwer, eben weil das Graal-Universum so viele Zielrichtungen verfolgt. Wie man das in 45 Minuten überhaupt vorstellen könnte – ich weiß es auch nicht.

Auch Stephan Frind versuchte sich unter dem Titel “Graal oder nicht Graal –ist das wirklich eine Frage?” an diesem Themenkomplex. Deutlich high-leveliger als Michael Wiedeking, und deshalb erfolgreicher beim Vermitteln des allgemeinen Überblicks. Leider aber dadurch eben auch fehlende Tiefe. Die Compilerbau-Details und die C1-C2-Abgrenzung in der JVM waren nicht immer ganz korrekt, aber das tat nicht weiter weh. Details, die nur einem passionierten i-Dipfeles-Scheißer auffallen. Die Abgrenzung Graal vs. GraalVM war auch hier eher unscharf, Schwächen der derzeitigen native image-Lösung kamen etwas zu kurz. Am Ende der Hinweis “Testen! GraalVM etwas schwieriger” könnte in der Kategorie “Untertreibung des Jahres” durchaus weit vorne landen. Man schaue sich die diversen Issues zum Thema native image auf GitHub an, um ein Gefühl für den präzisen Stand der Dinge bezüglich Stabilität und Umfang zu bekommen.

Aufgrund sparsamer Konkurrenz im Zeitslot verschlug es mich auch in “Aus eins mach zehn: Neuentwicklung mit Microservices” von Lars Alvincz und Bastian Feigl (andrena objects ag). Im Abstract war schon angekündigt, dass auch “Null vermeiden” ein Thema sein sollte, und das störte doch gewaltig – wie kann man etwa 10 Minuten der wertvollen Zeit auf so eine Nichtigkeit verschwenden, wo es doch um das Thema “wir lösen ein monolithisches System durch Microservices ab” ging? Mit all seinen interessanten Fragestellungen von “sind Microservices der richtige Ansatz” über “was hat die Neuentwicklung veranlasst” bis zu “das war der Ressourcenbedarf an CPU und Speicher vorher und nachher”. Neben den Klassikern “wie schneide ich die Services richtig” und “wie schaffe ich es, transaktionales DB-Verhalten mit dem Microservice-Umfeld zu verheiraten” natürlich. Es wurde nicht mal ansatzweise versucht, hier mehr als ein paar Worte zu den interessanten Teilgebieten zu verlieren. Besonders ärgerlich, weil die dargestellten Anforderungen von Kundenseite keineswegs eine Microservice-Architektur nahelegten oder gar erzwangen. Warum bei einer eher trivialen Standardanwendung, zudem mit zwingender Datenmigration und klarer Vorgabe an Funktionalität ein “Big Up Front-Design nicht sinnvoll/möglich” gewesen sein soll – behauptet wurde es, aber begründet nicht. Zusätzlich fiel mir wieder auf, dass die Lösung mit zwei Vortragenden nur ganz selten wirklich gut funktioniert. Auch die Themen “Versionsverwaltung des Codes” und “Trunk-based development vs. Feature Branches” wurde kurz gestreift, ohne dass klar wurde, warum das in diesem Kontext unbedingt Erwähnung finden musste. Insgesamt kein überzeugender Vortrag. In keinem der behandelten Unterthemen.

Da mein tägliches Brot immer noch Java 8 ist (böse Zungen würden behaupten, dass man in Java 8 natürlich immer noch genauso wie in Java 1.4 programmieren kann und schon die Einführung von Generics ein Fehler war) und ich die Neuerungen aus Java 9 bis Java 13 zwar kenne, aber nicht seriös im produktiven Einsatz habe, habe ich mir “Power Catch-up: Alles Praktische und Wichtige aus Java 9 bis 12” von Benjamin Schmid angeschaut. Sehr komprimiert, ohne Füllstoff, kompakt und doch mit Mehrwert gegenüber dem bloßen Lesen von Artikeln zum Thema. Ich war zufrieden. Auch wenn einzelne Themen wie die neuen GC-Varianten oder Project Loom natürlich in 45 Minuten nicht annähernd in gebührender Ausführlichkeit behandelt werden können. Der Vortragende hat sich aber mit potenziell verwirrenden Details angenehm zurückgehalten. Sehr gut.

Und das Beste kommt zum Schluss. “RxJava2: von 0 auf 100(?) in 45 Minuten” von und mit Johannes Schneider. Sensationell. Einer der besten Vorträgem egal zu welchem Thema, den ich je auf einer Konferenz gehört habe. Genau getimed auf die 45 Minuten, professionell und trotzdem humorvoll vorgetragen, voller Anregungen und Informationen. Didaktisch vom Feinsten. Ich muss gestehen, dass das den 2017er Vortrag zur reaktiven Programmierung, den ich anno dazumal gar nicht so schlecht fand, im Nachhinein ziemlich blass wirken lässt, Mein Kompliment an Johannes Schneider.

Was bleibt hängen? Zwei Dinge sollte man sich (ich mir) dringend im Detail anschauen: natürlich Graal(VM), und RxJava/ReactoveX, gerne auch im Swing-Kontext (auch wenn meine heimische Library natürlich die allermeisten Dinge schon auf anderem Wege erreicht – aber mit RxJava gäbe es hier die Möglichkeit, die Code-Lesbarkeit zu verbessern). Dafür kann man glatt seine Vorbehalte gegen 3rd-party-Abhängigkeiten über Bord werfen.