I’ve written several blog posts that list out all the changes for each of the most recent releases of Java (JDK 10, JDK 11). With JDK 12 just having been released, it seemed the obvious thing to produce another blog in this series. I’ll be doing the flip side of this later, which is the pitfalls that might cause you problems, should you want to migrate an application to use this version.

We are now well into the new six-monthly release cadence, and everything is working smoothly. I represent Azul on the Java SE JSR Expert Group, and we decided to switch this version to the revised JSR process. This is not radically different, more of a streamlining of the process to fit into the available time more easily.

As you will see, JDK 12 clearly has the least new features of any Java release to date (I counted 109 in JDK 10 and 90 in JDK 11). This is most definitely not a negative; because of the time-driven delivery process, some versions will contain plenty of changes, some will provide less.

I’ll break things down into the obvious logical areas: Java language, libraries, JVM and other JDK features.

Language Changes

What I (and I assume many other people will) consider the most prominent new feature in JDK 12 is switch expressions (JEP 325). This is also the first language change to be included as a preview language feature. The idea of preview language features was introduced at the start of 2018, under JEP 12. Essentially, this is a way of including beta versions of new features. By doing this, it is still possible to make changes based on user feedback and, in an extreme case, remove the feature altogether if it is not well received. The key point about preview features is that they do not get included in the Java SE specification.

Prior to JDK 12, switch was a statement, which to give it a proper definition is a syntactic unit that expresses some action to be carried out. In JDK 12 it has become an expression, which evaluates the contents of the switch to produce a result. One thing to make clear straight away is that it does not impact backward compatibility, so you do not need to change any code you have that uses switch as a statement.

The reason for this change is that there is a widespread pattern in the use of switch. Frequently we use a switch to effectively map from the value being switched on to assigning the value of a variable. I’ll use the example from the JEP since that is both straightforward and easy to understand.

int numLetters; switch (day) { case MONDAY: case FRIDAY: case SUNDAY: numLetters = 6; break; case TUESDAY: numLetters = 7; break; case THURSDAY: case SATURDAY: numLetters = 8; break; case WEDNESDAY: numLetters = 9; break; default: throw new IllegalStateException("Huh? " + day); }

As you can see, we’re mapping from the day of the week to the length of the name of that day, which is assigned to numLetters. Now that switch is a statement we can make the assignment once (reducing the potential for erroneous code greatly) using the result of the switch statement:

int numLetters = switch (day) { case MONDAY, FRIDAY, SUNDAY -> 6; case TUESDAY -> 7; case THURSDAY, SATURDAY -> 8; case WEDNESDAY -> 9; default -> throw new IllegalStateException("Huh? " + day); };

You’ll quickly spot two changes to the syntax we’re all used to. Firstly, we have finally moved away from each case needing to be separated. The OpenJDK developers have stumbled across a little know syntactic feature called a ‘comma separated list’. Finally! Secondly, the Lambda expression operator, ->, has been borrowed to simplify how we show what value to return from the switch. You can use break with a value if you really want to. There are a couple of other details about this feature, but it is probably easier to read the JEP.

Libraries

There is one change that I think is very useful. There are a number of minor ones as well.

The Streams API has a new Collector, as usual, provided by the Collectors utility class. The new collector can be obtained using the teeing() method. The teeing collector takes three arguments, two Collectors and a BiFunction. To understand what this does I’ve drawn a diagram:

All the values from the input stream are passed into each Collector. The result of each Collector is passed as arguments to the BiFunction to evaluate and generate a final result.

A simple example is one that calculates an average (yes, I know there are already Collectors for this like averagingInt(), but it’s an easy example to help understand the concept).

/* Assume Collectors is statically imported */ double average = Stream.of(1, 4, 2, 7, 4, 6, 5) .collect(teeing( summingDouble(i -> i), counting(), (sum, n) -> sum / n));

The first collector calculates the sum of the input stream while the second counts the number of elements. The BiFunction divides the sum by the element count to evaluate the average.

java.io

InputStream skipNBytes(long n): This skips over and discards exactly n bytes of data from this input stream. If n is zero or less, then no bytes are skipped.

java.lang

There is a new package, java.lang.constant, which is part of the JVM Constants API, JEP 334.

Every Java class file has a constant pool, which stores the operands for bytecode instructions in the class. For developers who want to manipulate class files (of which there won’t be that many), this is complicated because of issues around class loading. The JVM Constants API provides symbolic reference types to describe each form of constant (class, loadable constant, MethodHandle, MethodHandle constant and MethodType constant). There is also an enumeration for the kinds of MethodHandle that can be described and three classes for descriptions, one of which is the somewhat oxymoronic, DynamicConstantDesc (which is it, constant or dynamic? Surely it can’t be both).

This has also affected several other classes. All the following classes now have a describeConstable() method:

Class

Double

Enum

Float

Integer

Long

String

MethodHandle

MethodType

VarHandle

As a Brit, I find this quite amusing. The term Constable has been used since the 11th Century and is how we often refer to policemen. It is also the name of a famous 18th Century artist, John Constable. It makes me wonder whether a future version will include a method, describeTurner().

Obviously, in this case, it is a contraction of Constant Table not referring to an officer of the law or a landscape artist.

Update: It seems I am wrong in my assumption that Constable is a contraction of Constant Table. A poster on Reddit pointed out that it is something that can be represented in the constant pool, thus const-able. I’m happy to stand corrected, although it would be simpler if the API designers could avoid contractions of constant so that the confusion between whether it is cons-table or const-able is avoided.

The following classes now include a resolveConstantDesc() method

Double

Enum.EnumDesc

Float

Integer

Long

String

java.lang.Character

Inner classes have been updated to include new Unicode blocks. I always like to see what the Unicode people have found to add so here are some examples:

Chess Symbols

Mayan numerals – An interesting base 20 numerical system

Sogdian – An Eastern Iranian language, which ceased to be used in the 11 th Century.

Century. Old Sogdian – An older (and, I suspect, of even more limited use) variation of Sogdian

java.lang.Class

arrayType() returns a Class for an array type whose component type is described by this Class. Using jshell to test this shows:

jshell> (new String[2]).getClass().getName() $11 ==> "[Ljava.lang.String;" jshell> (new String[2]).getClass().arrayType() $12 ==> class [[Ljava.lang.String; jshell> "foo".getClass().arrayType() $15 ==> class [Ljava.lang.String;



I’m not totally sure what the point of this method is since all it does is prepend class [ to whatever type the Class represents.

componentType(), which is the same as getComponentType(), which begs the question, why add a redundant method?

descriptorString(). Again, this returns the same result as getName(). However, it is required as Class now implements the TypeDescriptor interface, which is related to the new JVM Constants API.

java.lang.String

indent(): adds a number of leading whitespace characters to the String. If the parameter is negative that number of leading whitespace characters will be removed (if possible).

transform(): Applies the provided function to the String. The result does not need to be a String.

java.lang.invoke

VarHandle now has a toString() method to return a compact description.

java.net.SecureCacheResponse and java.net.ssl.HttpsConnection both have a new method, getSSLSession(), which returns an Optional containing the SSLSession in use on the connection.

java.nio.files

The Files class has a new method, mismatch(), which finds and returns the position of the first mismatched byte in the content of two files, or -1L if there is no mismatch.

java.text

There is a new class, CompactNumberFormat. This is a subclass of NumberFormat that formats a decimal number in a compact form. An example of a compact form would be writing 1,000,000 as 1M, thus requiring two instead of nine characters. NumberFormat and java.text.spi.NumberFormatProvider have been extended to include a new method, getCompactNumberInstance(). There is also a related new enumeration, NumberFormatStyle, which has two values: LONG and SHORT.

java.util.concurrent

CompletionStage now include several overloaded forms of three methods:

exceptionallyAsync

exceptionallyCompose

exceptionallyComposeAsync

These methods extend how a new CompletionStage can be created from an existing CompletionStage if the current one completes exceptionally. Check the API documentation for the details.

javax.crypto

The Cipher class has a new toString() method, which returns a string containing the transformation, mode, and provider of the Cipher.

javax.naming.ldap.spi

This is a new package in JDK 12 and contains two classes, LdapDnsProvider, which is a service-provider class for DNS lookups when performing LDAP operations. LdapDnsProviderResults encapsulates the result of a DNS lookup for an LDAP URL.

Swing

Swing is still being updated! Yes, the filechooser.FileSystemView has a new catchy method, getChooserShortcutPanelFiles(). This returns an array of files representing the values to show by default in the file chooser shortcuts panel.

JVM Changes

JEP 189: Shenandoah: A Low-Pause-Time Garbage Collector

Shenandoah is a research project announced by Red Hat in 2014 that targets low-latency application requirements for memory management on the JVM. The design is to be concurrent with application threads so avoiding the problems we see with most (but certainly not C4 in Zing) garbage collectors.

This is now included in the OpenJDK, but not in all builds. It seems that neither the Oracle JDK nor the Oracle OpenJDK JDK builds include this. This is quite acceptable because it is not necessary to include Shenandoah to pass the TCK and conform to the Java specification. (Strictly speaking, it is not necessary to have any garbage collector, which is why the Epsilon collector is valid).

This is a change that is designed to make the G1 collector better at achieving a specified latency target. G1 divides the heap space (both young and old) into regions. The idea behind this is that the old generation does not need to be garbage collected in one operation. When G1 needs to collect, it selects the regions that it determines need to be collected. This is called the collection set. Prior to JDK 12, once work started on a collection set, then all work had to be completed, essentially as an atomic operation. The problem with this was that sometimes, due to changes in how the application was using heap space, the collection set ends up being too big and taking too long to collect resulting in the pause time goal not being met.

In JDK 12, if G1 identifies this situation, it will abort the collection partway through the set if this does not impact on the ability of the application to continue to allocate space for new objects. The net effect is G1 will be better at achieving the pause time goal.

This is another performance improvement for G1 but one more related to how the JVM interacts with the rest of the system. The JVM heap obviously requires memory and when it starts will request memory from the underlying operating system virtual memory allocator. As the application runs, there may be times where the amount of memory required by the heap drops and some of the committed memory can be returned to the operating system to be used by other applications.

G1 already does this but can only do it in one of two places. Firstly, during a full collection and secondly during one of the concurrent cycles. G1 tries hard not to do a full collection, and with low memory usage, there may be significant periods between collection cycles. This leads to G1 retaining committed memory potentially for a long time.

In JDK 12, G1 will, during inactivity of the application, periodically try to continue or trigger a concurrent cycle to determine overall Java heap usage. Unused memory can be returned to the operating system in a more timely and predictable manner.

A new command line flag, -XX:G1PeriodicGCInterval, can be used to set the number of milliseconds between checks.

This feature will lead to more conservative memory utilisation by the JVM for applications that become idle for extended periods.

Other JDK New Features

The Java Microbenchmarking Harness (JMH) was developed by Aleksey Shipilëv when he was at Oracle and provides a rich framework for developing performance benchmarks for Java applications. Aleksey has done an outstanding job of helping people to avoid many of the simple mistakes they make when trying to analyse application performance: warm up, avoiding code elimination that won’t happen in production, etc.

This has now been included in OpenJDK. Anyone who is interested in working on the JDK itself and modifying code can use this to compare performance, both before and after their changes but also to see how performance compares across releases. A range of benchmarks are included to enable testing; the design of JMH is such that it is straightforward to add new ones, where required.

OpenJDK has two ports for the Arm 64 architecture, one provided by Oracle the other from Red Hat. Since this was unnecessary and Oracle had stopped supporting Arm for their JDK binaries, the decision was made to use only the Red Hat port, which is still being maintained and developed.

Class Data Sharing (CDS) used to be a commercial feature in the Oracle JDK. With the recent move, completed in JDK 11, to eliminate all functional differences between the Oracle JDK and the OpenJDK, this is now included in OpenJDK.

To use CDS, an archive is required that has been generated for classes that are loaded when an application starts. In JDK 12, for 64-bit platforms, there is now a classes.jsa file in the lib/server directory. This is the CDS archive for the “default classes”. I assume this means all the public classes in the JDK modules; I couldn’t find a way to unpack this to check. Since CDS is turned on by default, equivalent to the -Xshare:auto option on the command line, users will benefit from improved startup time for applications from this.

Conclusions

JDK 12 provides a small number of new features and APIs, with the switch expression being the most interesting to developers. Users of G1 will no doubt appreciate the performance improvements.

With the new release cadence, I would advise all users to test their applications on this release. Keeping up with the gradual changes will avoid surprises if you decide to move to a subsequent long-term support release.

We have Zulu Community Edition builds of JDK 12 available completely free of charge to help with your testing. Why not give it a try?