Java 10 – Evolution statt Revolution

Die Zukunft von Java

Gerade erst ist Java 9 erschienen, da steht auch schon das nächste Release, Java 10, an. Dieser Artikel betrachtet die Neuerungen von Java 10 und wagt einen kurzen Blick in die Zukunft.

Eigentlich ist man als Java-Entwickler gewohnt, mehrere Jahre auf ein neues Java-Release zu warten und sich anschließend an ein paar großen und vielen kleinen Erweiterungen und Verbesserungen zu erfreuen. Doch mit dem Release von Java 9 hat Oracle auch die Release-Planung von Java verändert. Diese sieht vor, dass ab Java 9 alle sechs Monate ein neues Major Release von Java erscheint. Diese Änderung beschert uns nun also jetzt, im März 2018, bereits Java 10.

Natürlich kann ein solches Release nicht mehr dieselbe Größe erreichen wie die bisherigen Releases. Der Vorteil liegt eher darin, konstant Neuerungen zu erhalten und nicht auf triviale kleine Verbesserungen lange warten zu müssen.

Im Folgenden betrachten wir zuerst die Neuerungen, die über Java Enhancement Proposals (JEPs) umgesetzt wurden. Anschließend schauen wir uns weitere kleine Änderungen an. Zum Schluss wage ich dann einen Ausblick auf Dinge, die möglicherweise in Java 11 oder später erscheinen könnten.

Local-Variable Type Inference

Java 10 besteht insgesamt aus zwölf neuen JEPs. Der für die meisten Entwickler wohl interessanteste ist 286.

Java gilt für viele als sehr geschwätzige Sprache. So muss an vielen Stellen der Typ auf der linken und auf der rechten Seite beschrieben werden, zum Beispiel bei der Erzeugung eines neuen Objektes. An einigen Stellen ist es nun möglich, diesen auf der linken Seite durch das Keyword var zu ersetzen und sich somit ein paar Zeichen und die Wiederholung der Typinformation zu sparen. Aus MyObject o = new Object(); wird somit var o = new MyObject();.

Diese Syntax kann jedoch nicht an allen Stellen genutzt werden, sondern wurde auf drei Fälle begrenzt. Der erste Fall bezieht sich auf lokale, also innerhalb einer Methode deklarierte Variablen. Hierbei kann var genutzt werden, solange der Variablen auch direkt ein Wert zugewiesen wird. Ein einfaches var foo; ohne Zuweisung ist also nicht möglich. Und selbst bei einer direkten Zuweisung gibt es Fälle, die nicht funktionieren, wie beispielsweise die Zuweisung einer Lambda-Funktion oder Methodenreferenz.

Die beiden anderen Fälle beziehen sich auf for-Schleifen. Hierbei kann sowohl für die Variable der for-each-Schleife als auch für die Zählvariable der normalen for-Schleife var genutzt werden.

Auf die Einführung eines zweiten Keywords, um Unveränderlichkeit anzuzeigen, wie const in JavaScript oder val in Kotlin, wurde bewusst verzichtet und es muss weiterhin auf die Verwendung von final zurückgegriffen werden. Eine unveränderliche lokale Variable kann also mit final var deklariert werden.

JDK Repository Konsolidierung

Innerhalb von JEP 296 wurde dafür gesorgt, dass sich das gesamte JDK nun innerhalb eines Mercurial Repositories befindet. Bisher war das JDK auf insgesamt acht verschiedene Repositories aufgeteilt: root, corba, hotspot, jaxp, jaxws, jdk, langtools und nashorn. Zwar war die Isolation der teilweise sehr verschiedenen Module gut, es brachte aber auch Probleme mit sich. Einerseits waren Bugfixes schwerer zu tracken, wenn mehrere Module geändert werden mussten, da man diese Bugfixes nicht mit einem modularen Commit machen konnte. Andererseits erleichtert diese Konsolidierung das spiegeln des JDKs nach Git und somit möglicherweise auch nach GitHub.

Änderungen beim Garbage Collector

JDK10 enthält zwei JEPs zum Thema Garbage Collector (GC). In JEP 304 ging es darum, ein sauberes Interface für einen GC zu definieren und den Build-Prozess von diesem zu erneuern. Bisher lagen die Quelldateien für einen GC quer verstreut im JDK und es war somit für Neulinge schwer, sich zurechtzufinden. Zudem war es nicht einfach möglich, bei einem Build des JDKs bestimmte GCs zu exkludieren. All dies ist nun dank der Umstrukturierung möglich.

Die zweite Änderung zum Thema GC findet man in JEP 307. Dieser ermöglicht es, dass der in JDK9 zum Standard deklarierte G1 (Garbage-First) GC im Falle einer vollen Collection parallel läuft. Ziel ist es, dass diese genauso schnell ist wie im vorherigen Default, dem Parallel Collector.

Startzeit und Speicherverbrauch

Der JEP 310 beschäftigt sich mit einem bereits seit Java 5 verfügbarem Feature, dem sogenannten Class-Data Sharing (CDS). Dieses erlaubt es der JVM, bestimmte Klassen in ein geteiltes Archiv zu legen. Dieses Archiv kann nun beim nächsten Start der JVM direkt in den Arbeitsspeicher gemappt werden und ermöglicht somit einen schnelleren Start. Dieses Feature war bis JDK9 alleine dem bootstrap Classloader vorbehalten und wird nun mit JDK10 auch allen anderen Classloadern ermöglicht.

Neben einer schnelleren Startzeit ermöglicht das CDS es, auch ein solches Archiv zwischen mehreren JVM-Prozessen auf einer Maschine zu teilen. Somit kann Arbeitsspeicher eingespart werden. Im JEP selbst beschreibt Oracle, dass bei einem Applikationsserver, der sechs JVM-Prozesse nutzt und insgesamt 13 GB an Arbeitsspeicher verbraucht, ca. 340 MB eingespart werden können.

Ein neuer JIT Compiler

Java nutzt zur Laufzeit einen Just-In-Time (JIT) Compiler, um Code, der häufig ausgeführt wird, nicht mehr im Bytecode auszuführen, sondern diesen in nativen Maschinencode zu übersetzen und anschließend diesen zu nutzen. Dies führt zu deutlichen Performanzgewinnen. Bereits im JDK9 wurde im Rahmen von JEP 295 ein in Java geschriebener Ahead-Of-Time (AOT) Compiler entwickelt. Dieser führt die Übersetzung in nativen Maschinencode nicht während der Laufzeit, sondern zuvor durch und kann somit nicht alle Optimierungen eines JIT durchführen, da einige Informationen erst zur Laufzeit vorhanden sind. Dieser, Graal genannte, Compiler kann nun dank JEP 317 auch experimentell als JIT genutzt werden. Da es allerdings nicht Ziel war, dieselbe Performanz wie mit den bisherigen JITs zu erreichen, sondern es eher darum geht zu experimentieren, was mit einem in Java geschriebenen JIT möglich ist, ist von einem Einsatz aktuell noch abzuraten.

Neues Versionierungsschema

Erst mit JDK9 hatte Oracle innerhalb des JEP 223 das Versionierungsschema standardisiert. Mit der Änderung der Release-Planung auf die nun sechsmonatigen Veröffentlichungen des JDKs wollte Oracle hier allerdings noch einmal nachbessern. Anfangs stand sogar die Idee im Raum, auf 9 nicht 10, sondern 18.3 folgen zu lassen. Dies wurde jedoch nach Protesten der Community fallen gelassen. Trotzdem sah Oracle noch Nachholbedarf und dieser wurde nun in JEP 332 umgesetzt.

Im Grunde bleibt das alte Schema bestehen, es ergeben sich jedoch leichte semantische Unterschiede. Die Versionsnummer besteht nun aus den vier Bestandteilen $FEATURE.$INTERIM.$UPDATE.$PATCH.

Feature wird dabei für jedes Feature-Release um eins erhöht. Ein solches Release darf sowohl neue Features hinzufügen als auch alte entfernen, sofern diese Entfernung im vorherigen Feature-Release angekündigt wurde. Mit der aktuellen Planung von sechs Monaten ergibt sich somit, das im September JDK11 folgt.

Der Interim-Teil wird für Releases genutzt, die keine neuen Features bringen, sondern nur kompatible Bug-Fixes und Erweiterungen enthalten. Aktuell wird dieser Teil nicht genutzt und ist somit immer 0.

Update wiederum wird verwendet, um Releases zu erzeugen, die kompatibel sind und Security, Regressionen und/oder Bugs fixen. Aktuell erscheint ein solches Release geplant einen Monat nach einem Feature-Release und anschließend alle drei Monate. Somit sollte im April bereits das JDK 10.0.1.0 erscheinen.

Als letzte Stelle bietet Patch die Möglichkeit, im Falle größerer Probleme, die unmittelbar gelöst werden müssen, ungeplante Releases zu erzeugen.

Weitere JEPs

Komplettiert wird JDK10 durch weitere fünf kleinere JEPs. Mit Thread-Local Handshakes lassen sich einzelne Threads von der JVM einfacher stoppen.

JEP 313 entfernt javah aus dem JDK. Dieses wurde genutzt, um beim Einsatz von JNI Dateien mit den nativen Headern zu erzeugen. Als Ersatz kann die bereits mit JDK8 in javac eingebaute Option -h genutzt werden.

Zudem werden dank JEP 314 nun zusätzliche Unicode-Language-Tag-Erweiterungen und mit JEP 316 alternative Speichermedien, wie NV-DIMM, für die Verwaltung des Heaps unterstützt.

Vervollständigt wird JDK10 durch eine Menge von Root-Zertifikaten. Bisher konnten diese nicht im OpenJDK ausgeliefert werden, da sie nicht unter einer Open-Source-Lizenz standen. Mit dem JEP 319 geht Oracle genau dieses Problem an und stellt die bisher im OracleJDK befindlichen Zertifikate zur Verfügung.

Weitere kleine Änderungen

Neben den großen Änderungen, die durch die JEPs umgesetzt werden, enthält ein JDK-Release auch eine Menge von kleineren Änderungen an den bestehenden APIs und Tools. Die folgenden Änderungen sind dabei nur eine Auswahl.

Führt man Java in Containern, zum Beispiel mit Docker, aus, so war es bisher so, dass die JVM Quotas, die man einem Container gegeben hat, ignorierte. So konnte man zwar dem Container sagen, dass er nur maximal 1 GB an Arbeitsspeicher zur Verfügung hatte, die JVM versuchte jedoch trotzdem, den gesamten auf dem Docker-Host zur Verfügung stehenden Arbeitsspeicher zu nutzen. Gleiches gilt für die Limitierung von CPU. Auch diese Problematik wurde im JDK10 angegangen. Nachvollziehen kann man die hierfür gemachten Änderungen und relevanten Flags des java-Tools in einem Ticket.

Weiterhin wurde Javadoc um ein neues Tag, @summary, erweitert, das es ermöglicht, gezielter auszuzeichnen, was man als Beschreibung in der Methodenübersicht anzeigen möchte. Bisher wurde hierzu lediglich der erste Satz des Javadoc-Kommentares verwendet.

Auch Collections und Streams wurden um eine Kleinigkeit erweitert. Nachdem bereits im JDK9 die statischen Factory-Methoden eingeführt wurden, welche unveränderliche Collections erzeugen, wurden nun weitere Methoden ergänzt, um unveränderliche Datenstrukturen zu erzeugen. Bisher standen hierzu einige Methoden in java.util.Collections zur Verfügung. Diese erzeugen jedoch nur eine unveränderliche Sicht, das heißt, ändert man anschließend die unterliegende Datenstruktur, so ändert sich auch die so erzeugte Sicht. JDK10 fügt nun in den Klassen java.util.Set, java.util.Map und java.util.List jeweils eine Methode copyOf ein. Diese erzeugt aus einer vorhandenen Datenstruktur eine neue unveränderliche, die sich auch nicht ändert, wenn man die vorhandene anschließend modifiziert.

Damit man solche unveränderliche Datenstrukturen auch bei der Nutzung von Streams erzeugen kann, wurden zusätzlich in der Klasse java.util.stream.Collectors die Methoden toUnmodifiableList, toUnmodifiableMap und toUnmodifiableSet hinzugefügt. Es ist somit nun möglich, mit JDK-Bordmitteln bequem wirklich unveränderliche Datenstrukturen zu erzeugen.

Zudem wurden die Klasse java.util.Optional und die Varianten für Primitive, java.util.OptionalDouble, java.util.OptionalInt und java.util.OptionalLong, um die Methode orElseThrow ohne Argumente erweitert. In diesem Falle wird bei einem leeren Optional eine NoSuchElementException geworfen.

Zu guter Letzt wurde auch das API des sich weiterhin im Inkubator befindlichen neuen HTTP2-Clients verbessert und ist sicher einen Blick wert. Wer gerne noch weiter schauen möchte, was sich in den bestehenden APIs so geändert hat, sei auf das GitHub-Projekt von Gunnar Morling verwiesen.

Ausblick in die Zukunft

Die bereits erwähnte Entscheidung, alle sechs Monate ein neues Release zu veröffentlichen, führt dazu, dass wir bereits im September das nächste JDK begrüßen dürfen. Bisher (Stand Februar 2018) sind dort vier JEPs als potenzielle Kandidaten gelistet, darunter zum Beispiel JEP 320, um alte APIs aus dem JDK zu entfernen. Es lohnt sich ab und an mal zu schauen, welche neuen JEPs hinzugefügt wurden, um einen Überblick zu erhalten, auf was man sich freuen darf.

Neben diesen konkret geplanten JEPs wird auch im Hintergrund fleißig an weiteren spannenden Dingen gearbeitet. Besonders interessant sind hierbei die Entwicklungen im sogenannten Projekt Amber. Aus diesem Inkubator werden in Zukunft vermutlich Sprachfeatures wie erweiterte switch-Statements und Enums, Pattern-Matching und Data-Classes in die Sprache Java integriert werden.

Außerdem stehen Ideen im Raum, Strings zu erweitern, um diese als mehrzeiliges Literal definieren zu können, oder Java-Quelldateien, die keine Abhängigkeiten zu Bibliotheken haben, direkt starten zu können, ohne diese vorher kompilieren zu müssen.

Zudem werden natürlich auch die existierenden APIs erweitert und verbessert, beispielsweise wird überlegt, java.lang.String um eine Methode repeat zu erweitern.

Fazit

JDK10 ist das erste Java-Release innerhalb der neuen verkürzten Zeitplanung. Als solches beinhaltet es deutlich weniger Änderungen, als man es bisher gewohnt ist. Alles in allem zeichnet es sich für Entwickler nur durch kleine Änderungen aus und lässt große Dinge vermissen.

Setzt man bereits auf das JDK9, so ist ein Upgrade jedoch Pflicht. Mit dem Erscheinen von JDK9 hat Oracle die Supportzeiträume geändert. JDK9 erhält mit dem Release von JDK10 nämlich keine Security Updates mehr. Lediglich JDK8 als sogenanntes LTS (Long Time Support) Release erhält parallel zu JDK10 noch Updates. Ist man also aktuell noch auf JDK8, so kann es sich lohnen, auf JDK11 im September zu warten, da dieses das nächste LTS-Release wird.

Und auch die Zukunft von Java verspricht, spannend zu bleiben. Das Core-Team arbeitet kontinuierlich daran, neue Features auf Sprachebene und neue APIs zur Verfügung zu stellen. Angesichts der Konkurrenz durch die vielen anderen JVM-Sprachen ist dies auch notwendig. Dabei wird jedoch weiterhin viel Wert auf Rückwärtskompatibilität gelegt, eine Qualität, die auch dafür gesorgt hat, dass Java eine so hohe Verbreitung erreichen konnte.

TAGS

Kommentare

Um die Kommentare zu sehen, bitte unserer Cookie Vereinbarung zustimmen. Mehr lesen