Here we go, JDK-17, the next LTS release, just became generally available. It is an important milestone for the OpenJDK for years to come but sadly, Project Loom, the most anticipated improvement of the JVM platform, was not able to make it, despite the extraordinary progress being made. Well, if you are still on JDK-8, like the majority of us, who cares, right?
Not really, for example Spring Framework had made an announcement quite recently to support JDK-17 as a baseline. It is very likely others will follow, leaving JDK-8 behind. So what is new in JDK-17?
JEP-306: Restore Always-Strict Floating-Point Semantics: makes floating-point operations consistently strict, rather than have both strict floating-point semantics (
strictfp
) and subtly different default floating-point semantics. This will restore the original floating-point semantics to the language and VM, matching the semantics before the introduction of strict and default floating-point modes in Java SE 1.2.JEP-382: New macOS Rendering Pipeline: implements a Java 2D internal rendering pipeline for macOS using the Apple Metal API as alternative to the existing pipeline, which uses the deprecated Apple OpenGL API.
JEP-391: macOS/AArch64 Port: ports the JDK to macOS/AArch64. Quick refresher, an AArch64 port for Linux was delivered in JDK-9 (JEP-237) and for Windows (JEP-388) in JDK-16.
JEP-403: Strongly Encapsulate JDK Internals: strongly encapsulates all internal elements of the JDK, except for critical internal APIs such as
sun.misc.Unsafe
. It will no longer be possible to relax the strong encapsulation of internal elements via a single command-line option, as was possible in JDK-9 through JDK-16. This JEP is logical continuation of the JEP-396, and pushes things even further:- The
sun.misc.Unsafe::defineAnonymousClass
method was removed. The replacements are java.lang.invoke.MethodHandles.Lookup::defineHiddenClass or java.lang.invoke.MethodHandles.Lookup::defineHiddenClassWithClassData, see please JDK-8266760 for more details. - The mechanism of the relaxed strong encapsulation, controlled by command line option
--illegal-access
becomes obsolete. Any use of this option, whether withpermit
,warn
,debug
, ordeny
, will have no effect other than to issue a warning message.
- The
JEP-409: Sealed Classes: enhances the Java programming language with sealed classes and interfaces. Sealed classes and interfaces restrict which other classes or interfaces may extend or implement them. The JEP introduces new
sealed
/non-sealed
modifiers andpermits
clause to the class or interface declaration.public abstract sealed class Result permits Success<?>, Failure { // ... }
The classes specified by
permits
clause must be located near the superclass: either in the same module (if the superclass is in a named module) or in the same package (if the superclass is in the unnamed module). Every permitted subclass must directly extend the sealed class and must use a modifier to describe how it propagates the sealing initiated by its superclass:final
,sealed
, ornon-sealed
.public final class Failure extends Result { public Throwable getCause() { // ... } }
public final class Success<T> extends Result { public T getResult() { // ... } }
Interestingly, there is more compact way to declare sealed class hierarchy in the same source file. This way, the sealed class may omit the
permits
clause, the permitted subclasses are going to be inferred by the compiler.public abstract sealed class Result { public final class Success<T> extends Result { public T getResult() { // ... } } public final class Failure extends Result { public Throwable getCause() { // ... } } }
As for classes, an interface can be sealed by applying the
sealed
modifier.public sealed interface Result permits Success<?>, Failure { // ... }
public final class Failure implements Result { public Throwable getCause() { // ... } }
public final class Success<T> implements Result { public T getResult() { // ... } }
Last but not least, sealed classes work well with record classes, which are implicitly
final
:public record Failure(Throwable cause) implements Result {}
public record Success<T>(T result) implements Result {}
The benefits of sealed classes are two-fold. They allow to express algebraic data types (ADTs) in Java as well as to enhance pattern matching for
switch
expressions in future JDKs.The java.lang.Class API has been enriched with additional methods to support sealed classes:
One new method has been added to TypeElement class as well:
To be noted, sealed classes are not the compiler trick. The JVM recognizes sealed classes and interfaces at runtime, and prevents extension by unauthorized subclasses and subinterfaces.
JEP-356: Enhanced Pseudo-Random Number Generators: provides new interface types and implementations for pseudorandom number generators (PRNGs), including jumpable PRNGs and an additional class of splittable PRNG algorithms (LXM).
The standard library contains Random / SecureRandom, ThreadLocalRandom, and SplittableRandom classes to deal with random number generation. All support pretty much the same set of methods however there are no relationships between them. That makes it difficult to abstract the random number generation logic. To address that, JEP provides a new interface, RandomGenerator, which supplies a uniform API for all existing and new PRNGs.
To learn more about the implementation details and new class hierarchy, I would highly recommend to check out excellent Better Random Number Generation in Java 17 article.
JEP-415: Context-Specific Deserialization Filters: allows applications to configure context-specific and dynamically-selected deserialization filters via a JVM-wide filter factory that is invoked to select a filter for each individual deserialization operation. The JEP is an improvement upon JEP-290, which introduced deserialization filters into JVM platform.
From the API standpoint, the java.io.ObjectInputFilter class had been extended to accommodate the required changes:
- static ObjectInputFilter allowFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus)
- static ObjectInputFilter rejectFilter(Predicate<Class<?>> predicate, ObjectInputFilter.Status otherStatus)
- static ObjectInputFilter merge(ObjectInputFilter filter, ObjectInputFilter anotherFilter)
- static ObjectInputFilter rejectUndecidedClass(ObjectInputFilter filter)
JEP-398: Deprecate the Applet API for Removal: deprecates the Applet API for removal. It is essentially irrelevant since all web-browser vendors have either removed support for Java browser plug-ins or announced plans to do so.
JEP-407: Remove RMI Activation: removes the Remote Method Invocation (RMI) Activation mechanism, while preserving the rest of RMI. It was deprecated for removal JDK-15 (JEP-385) and completely gone in JDK-17.
JEP-410: Remove the Experimental AOT and JIT Compiler: removes the experimental Java-based ahead-of-time (AOT) and just-in-time (JIT) compiler. This compiler has seen little use since its introduction and the effort required to maintain it is significant. Retain the experimental Java-level JVM compiler interface (JVMCI) so that developers can continue to use externally-built versions of the compiler for JIT compilation (for example, GraalVM).
JEP-411: Deprecate the Security Manager for Removal: deprecate the Security Manager for removal in a future release. The Security Manager dates from Java 1.0. It has not been the primary means of securing client-side Java code for many years, and it has rarely been used to secure server-side code. To move Java forward, we intend to deprecate the Security Manager for removal in concert with the legacy Applet API (JEP-398).
This is probably the most debated and controversial change so far since Security Manager is being used more widely than it was claimed. Apparently, the Security Manager popped up as a real showstopper for Project Loom, spawning Security and Sandboxing Post SecurityManager to justify the decision.
JDK-8251989: Hex formatting and parsing utility. Introduces new utility class java.util.HexFormat that converts between bytes and chars and hex-encoded strings which may include additional formatting markup such as prefixes, suffixes, and delimiters. Dustin Marx has done a great job illustrating this new API usage.
JDK-8235139: Deprecate the socket impl factory mechanism: proposes to deprecate, and eventually remove, the API points for statically configuring a system-wide factory for the socket types in the java.net package.
JDK-8266369: Add a new Selector implementation for the Windows platform based on the "wepoll" library. The main benefit is that the cost of selection operations are a function of the number of sockets that are ready for I/O rather than the number of channels registered with the Selector.
JDK-8267077: Unified Logging Supports Asynchronous Log Flushing. To avoid undesirable delays in a thread using unified logging, it is now possible to request the unified logging system to operate in asynchronous mode by passing the command-line option
-Xlog:async
. In asynchronous logging mode, log sites enqueue all logging messages to a buffer and a dedicated thread is responsible for flushing them to the corresponding outputs. The intermediate buffer is bounded so in case of overflow, the enqueuing messages are discarded. The size of the intermediate buffer could be configured using the command-line option-XX:AsyncLogBufferSize=<bytes>
.A whopping amount of improvements went into java.lang.invoke package:
- JDK-8263087: Add a MethodHandle combinator that switches over a set of MethodHandles
- JDK-8264288: Performance issue with MethodHandle.asCollector
- JDK-8255493: Support for pre-generated java.lang.invoke classes in CDS dynamic archive (improves start-up time by storing pre-generated LambdaForm handlers in an application-specific CDS archive)
A ton of goodies has been delivered from the standard library perspective. Let us start from ForkJoinPool class, which now provides dedicated implementation for:
The java.util.Map.Entry got quite useful utility method:
A new interface java.time.InstantSource was introduced, which provides access to the current instant and is implemented by java.time.Clock.
The java.net.DatagramSocket has been enriched with better multicast group support:
The java.lang.invoke.MethodHandles has one new method:
The java.io.Console now returns the Charset object used:
The java.io.FileInputStream introduced own implementations of:
Arguably, the largest amount of improvements went into java.lang.Process class:
And what is happening on the security side of things? Besides SecurityManager deprecation (JEP-411), it is worth to mention:
- JDK-8217633: Configurable extensions with system properties
- JDK-8196415: Disable SHA-1 Signed JARs
- The default for security handshakes in JDK-17 is TLS 1.3 now, the previous versions of Java used TLS 1.2 (more details here)
JDK-17 is an important release. First of all, it is going to become the de-facto choice for green field projects for the next couple of years. Second, the ones who are still on JDK-8 would migrate straight to JDK-17, skipping JDK-11 entirely (there are hardly any reasons to migrate to JDK-11 first). Consequently, the third, migration from JDK-11 to JDK-17 requires comparatively low efforts (in most cases), it is logical to expect seeing the JDK-17 kicking out JDK-11.
Are we done yet? Luckily, not at all, the JDK release train is accelerating, shifting from three years LTS releases to two years. Hopefully, this strategic decision would speed up the pace of migration, specifically for enterprises, where many are stuck with older JDKs.
And let us not forget, the JDK-18 is already in early access! It comes with the hope that Project Loom is going to be included, in some form or the other.