Chasing Java’s release train, from 8 to 16. Part 2: The race to the next LTS release
In the first part we thoroughly went through the massive amount of features delivered in scope of JDK-9. Nevertheless, this release was always considered as being transitional, with little or no adoption expected. It has a mission to kick off the race towards next LTS release, JDK-11.
JDK 10
JDK-10, the first release followed the six months cadence cycle, brought a number of new features into the language and JVM itself. Let us take a look at the most interesting ones from the developer’s perspective.
- JEP-286: Local-Variable Type Inference: enhances the Java language to extend type inference to declarations of local variables with initializers. It allows the reserved type name
var
to be accepted in place of manifest types for local variable declarations with initializers, enhanced for-loop indexes, and index variables declared in traditionalfor
loops:1var listOfMaps =
new
ArrayList<Map<String, String>>();
Or, enforcing immutability:
1final
var listOfMaps =
new
ArrayList<Map<String, String>>();
If used carefully, the local-variable type inference leads to more compact and concise code, drastically improving its readability, like for example within try-with-resources blocks or loops.
123try
(var out =
new
ByteArrayOutputStream()) {
out.write(...);
}
1234var listOfMaps =
new
ArrayList<Map<String, String>>();
for
(var e: listOfMaps) {
// ...
}
The counterexample would be to strip valuable type information altogether by combining
var
and diamond operator<>
, likely abusing the feature.1var listOfMaps =
new
ArrayList<>();
The fair amount of controversy and confusion around local-variable type inference resulted into publication of the FAQ for JEP 286 which I would highly recommend to read over.
- JEP-316: Heap Allocation on Alternative Memory Devices: enables the HotSpot VM to allocate the Java object heap on an alternative memory device, such as an NV-DIMM, specified by the user. The new flag -XX:AllocateHeapAt=<path> has been added to support such memory device(s).
- JEP-310: Application Class-Data Sharing: to improve startup and footprint, extends the existing Class-Data Sharing (“CDS”) feature to allow application classes to be placed in the shared archive. By and large, Class-Data Sharing is probably the least known feature of JDK (the story goes back to JavaSE 5.0!) which nonetheless is quite handy in many circumstances and could significantly reduce the application startup time (obviously it depends a lot on your application). In scope of this JEP, there are 3 steps to take:
- Determine the classes to archive:1
$ java -Xshare:off -XX:+UseAppCDS -XX:DumpLoadedClassList=app.lst -cp app.jar AppMain
- Create the AppCDS archive:1
$ java -Xshare:dump -XX:+UseAppCDS -XX:SharedClassListFile=app.lst -XX:SharedArchiveFile=app.jsa -cp app.jar
Please note that we do not launch the application but just provide the complete classpath instead.
- Use the AppCDS archive:1
$ java -Xshare:on -XX:+UseAppCDS -XX:SharedArchiveFile=app.jsa -cp app.jar AppMain
One of the limitation of this JEP is that CDS cannot archive classes from user-defined modules (such as those specified in –module-path) but the good news are that CDS will get more improvements along JDK-12 and JDK-13 releases, stay tuned.
- Determine the classes to archive:
- JEP-319: Root Certificates: provides a default set of root Certification Authority (CA) certificates in the JDK. Essentially, it means the cacerts keystore will be pre-populated with a set of root certificates issued by the CAs of Oracle’s Java SE Root CA Program as such reducing the differences between OpenJDK and Oracle JDK builds.
- JEP-322: Time-Based Release Versioning: revises the version-string scheme of the Java SE Platform and the JDK, and related versioning information, for present and future time-based release models. The suggested version number format is:1
[
1
-
9
][
0
-
9
]*((\.
0
)*\.[
1
-
9
][
0
-
9
]*)*
The version number might be possibly followed by pre-release, build, and other optional information. This new semantics of version numbers is certainly working well so far.
- JEP-307: Parallel Full GC for G1: improves G1 worst-case latencies by making the full GC parallel.
- JEP-312: Thread-Local Handshakes: introduces a way to execute a callback on threads without performing a global VM safepoint. Make it both possible and cheap to stop individual threads and not just all threads or none. The feature was controlled by new flag -XX:ThreadLocalHandshakes (default value true) which however was deprecated in JDK-13 since turning it off became not viable from the performance perspective.
- JEP-304: Garbage-Collector Interface: improves the source code isolation of different garbage collectors by introducing a clean garbage collector (GC) interface. The motivation is very simple: make it much easier to implement new collectors and in fact a number of new GC collectors would greatly benefit from this change in later releases.
- JEP-313: Remove the Native-Header Generation Tool (javah): removes the javah tool from the JDK since it has been superseded by superior functionality in javac (JDK-7150368).
- JEP-317: Experimental Java-Based JIT Compiler: enables the Java-based JIT compiler, Graal, to be used as an experimental JIT compiler on the Linux/x64 platform. Sadly, the story of Graal in OpenJDK comes to the end in JDK-17.
- Multiple Collectors enhancements to deal with unmodifiable collections:
- The List got a new static method:
- The Set got a new static method:
- The Map got a new static method:
- The family of Optional classes (OptionalDouble, OptionalInt, OptionalLong) got a new method:
- The JarFile, along with JarEntry, are finally capable to provide proper support of the multi-release JARs, introduced in JDK-9:
- The Channels class got two new overloads of the existing static methods:
- The MethodType was enriched with:
- The Reader got quite handy new method:
- A new method was introduced into FileStore class:
- A number of new additions went into StampedLock class in a form of static methods:
- The Version class was enhanced to reflect the JEP-322: Time-Based Release Versioning changes:
- After many years, it became possible to get a PID of the running Java virtual machine using the APIs from standard library, thanks to the new method added to RuntimeMXBean:
- The ThreadMXBean was extended with new overloaded methods:
- Minor by handy addition to DateTimeFormatter:
Undoubtedly, JDK-10 release has quite moderate amount of features comparing to JDK-9, but every one of those was delivered much faster, thanks to the new release cycle.
JDK 11
The first LTS release of the JDK following the new schedule, JDK-11, had seen the light in 2018, six month after JDK-10 release. It finally brought a long awaited stability and established a new baseline in post JDK-9 world. It also included a number of features.
- JEP-327: Unicode 10: upgrades existing platform APIs to support version 10.0 of the Unicode Standard. In summary, Unicode 10 adds 8,518 characters, for a total of 136,690 character, notably:
- Bitcoin sign
- 56 emoji characters
- A set of Typicon marks and symbols
- JEP-331: Low-Overhead Heap Profiling: provides a low-overhead way of sampling Java heap allocations, accessible via JVMTI. The JEP brings significant improvements into troubleshooting application memory issues by capturing the call site for particular allocations.
- JEP-332: Transport Layer Security (TLS) 1.3: implements version 1.3 of the Transport Layer Security (TLS) Protocol, as per RFC-8446.
- JEP-329: ChaCha20 and Poly1305 Cryptographic Algorithms: implements the ChaCha20 and ChaCha20-Poly1305 ciphers as specified in RFC-7539. Additionally, the ChaCha20-Poly1305 is opening a door to use AEAD-based cipher suites along with TLS 1.3, nicely complementing JEP-332.
- JEP-181: Nest-Based Access Control: introduces nests, an access-control context that aligns with the existing notion of nested types in the Java programming language. Nests allow classes that are logically part of the same code entity, but which are compiled to distinct class files, to access each other’s private members without the need for compilers to insert accessibility-broadening bridge methods. The best way to understand the changes delivered by this JEP is to look at one of the examples.010203040506070809101112
public
class
Outer {
public
static
class
Inner {
public
void
print(Outer o) {
System.out.println(
"Inner"
);
o.print();
}
}
private
void
print() {
System.out.println(
"Outer"
);
}
}
The nested Inner class, the logical part of the Outer class, has access to its private methods. How is it possible? The compiler would generate the bridge methods for you, visible in bytecode.
12345678$ javap target/classes/com/example/Outer.
class
Compiled from
"Outer.java"
public
class
com.example.Outer {
public
com.example.Outer();
public
static
void
main(java.lang.String[]);
static
void
access$
0
(com.example.Outer);
}
Now, when you compile the same class using JDK-11, the first thing you will notice is that bridge method access$0 is gone.
1234567$ javap target/classes/com/example/Outer\$Inner.
class
Compiled from
"Outer.java"
public
class
com.example.Outer$Inner {
public
com.example.Outer$Inner();
public
void
print(com.example.Outer);
}
Besides changes in JVM and bytecode, there are a number of new methods added to Class class to reflect the concept of nests and nestmates:
For existing applications and/or libraries, these changes should come at no risk unless access bridge methods are explicitly exploited (by all means, doubtful idea in the first place).
- JEP-321: HTTP Client (Standard): standardizes the incubated HTTP Client API introduced in JDK-9, via JEP-110, and updated in JDK-10. The API is consolidated under the java.net.http package and consists of:
- class HttpClient
- class HttpHeaders
- class HttpRequest
- class HttpRequest.BodyPublishers
- class HttpResponse.BodyHandlers
- class HttpResponse.BodySubscribers
The APIs are concise and easy to use, the snippet below is a convincing example of how intuitive it is (you probably have seen this builder style in many other HTTP clients out there).
010203040506070809101112131415161718final
HttpClient client = HttpClient
.newBuilder()
.version(Version.HTTP_2)
.connectTimeout(Duration.ofMillis(
500
))
.followRedirects(Redirect.NEVER)
.build();
final
HttpRequest request = HttpRequest.newBuilder()
.POST(BodyPublishers.ofString(
"..."
, StandardCharsets.UTF_8))
.header(
"Content-Type"
,
"application/json"
)
.build();
final
Stream<String> lines = client
.send(request, BodyHandlers.ofLines())
.body();
// ...
Not to forget the asynchronous flavor, based on CompletableFutures.
12345final
CompletableFuture<Stream<String>> lines = client
.sendAsync(request, BodyHandlers.ofLines())
.thenApply(HttpResponse::body);
// ...
And obviously, the reactive style, using the APIs introduced by JEP-266 in JDK-9:
12345final
Subscriber<String> subscriber = ...;
final
CompletableFuture<HttpResponse<Void>> response = client
.sendAsync(request, BodyHandlers.fromLineSubscriber(subscriber));
// ...
The HTTP Client supports HTTP/1.1, HTTP/2 (HTTP/2 is the default preferred protocol, and the implementation seamlessly fallbacks to HTTP/1.1 if necessary) and Websockets. The Introduction to the Java HTTP Client is a good starting point to quickly surface the capabilities of the APIs.
- JEP-323: Local-Variable Syntax for Lambda Parameters: allows
var
to be used when declaring the formal parameters of implicitly typed lambda expressions. This rather small JEP delivers a substantial convenience to labmda expressions.123final
Comparator<String> comparator = (
@Nonnull
var s1,
@Nonnull
var s2) -> {
return
...;
};
A lambda expression may be implicitly typed, nothing new here, however when you want to decorate its parameters with annotations, it used to require providing the explicit types. With this JEP, not anymore, the
var
could be used instead. Please note that an implicitly typed lambda expression must usevar
for all its formal parameters or for none of them. - JEP-328: Flight Recorder: provides a low-overhead data collection framework for troubleshooting Java applications and the HotSpot JVM. Flight Recorder has existed for many years and was previously a commercial feature of the Oracle JDK but since JDK-11, it has been open-sourced (and backported to JDK-8). The Flight Recorder command line tool, jfr, which appeared only in JDK-12, was also backported to JDK-11 and is available as of 11.0.6 release. The Flight Recorder could be activated in a different ways:
- Using JVM command line arguments:1
$ java -XX:StartFlightRecording=settings=profile,duration=6m,name=app-startup,dumponexit=
true
,filename=/var/log/jfr/app-startup.jfr ...
- Using jcmd command line tool:
123$ jcmd <pid> JFR.start settings=profile duration=6m name=app-startup
$ jcmd <pid> JFR.dump filename=app-startup.jfr
$ jcmd <pid> JFR.stop
- Using JDK Mission Control
The Flight Recorder recordings could be visualized in JDK Mission Control or analyzed from the command line using jfr tool. To be fair, these days the Flight Recorder is a primary go-to tool for troubleshooting JVM applications in production.
- Using JVM command line arguments:
- JEP-330: Launch Single-File Source-Code Programs: enhances the java launcher to run a program supplied as a single file of Java source code, including usage from within a script by means of “shebang” files and related techniques. Who would have thought that one day Java would replace your favorite shell scripts? Well, since JDK-11 you could!
- Launch a class declared in a source file:1
$ java MyScript.java
- A shebang file to invoke the Java launcher using source-file mode:1
#!/path/to/java --source <version>
Please note that in this case the file should not be named as a Java source file (i.e. it should not be a file whose name ends with
.java
)
The JEP inspired a number of innovative projects, like jbang f.e., to simplify launching
.java
files from literally anywhere. - Launch a class declared in a source file:
- JEP-320: Remove The Java EE and CORBA Modules: removes the Java EE and CORBA modules from the Java SE Platform and the JDK. The following modules have been removed:
- java.xml.ws: replacement javax.xml.ws:jaxws-api, javax.xml.soap:javax.xml.soap-api, javax.xml:webservices-api
- java.xml.ws.annotation: replacement javax.annotation:javax.annotation-api
- java.xml.bind: replacement javax.xml.bind:jaxb-api
- java.activation: replacement javax.activation:javax.activation-api
- java.corba: replacement JacORB
- java.transaction: replacement javax.transaction:javax.transaction-api
Since Java EE has been superseded by Jakarta EE, all most recent replacements could be found under new Jakarta brand.
- JDK-8250784: Shenandoah: A Low-Pause-Time Garbage Collector: the Shenandoah GC has been backported to JDK-11 and is available in most distributions since 11.0.9 release.
- JDK-8191369: NMT: Enhance thread stack tracking: great improvement to native memory tracking.
- Probably, the String class had the largest number of new API methods added:
- The family of Optional classes (OptionalDouble, OptionalInt, OptionalLong) got a single new method:
- The Pattern has gotten one more method to support Predicate for matches, exceptionally convenient:
- The Predicate in turn could now be negated:
- The ByteArrayOutputStream could write the complete content now:
- The InputStream got a few additions:
- The OutputStream was also not left out:
- The Reader followed the same route:
- As well as the Writer:
- The CharSequence was enriched with lexicographical comparison:
- A whole family of buffer classes got the mismatch detection support:
- ByteBuffer was added int mismatch(ByteBuffer that)
- CharBuffer was added int mismatch(CharBuffer that)
- DoubleBuffer was added int mismatch(DoubleBuffer that)
- FloatBuffer was added int mismatch(FloatBuffer that)
- LongBuffer was added int mismatch(LongBuffer that)
- ShortBuffer was added int mismatch(ShortBuffer that)
- The SelectionKey got a few atomic operations:
- The Selector had a number of overloaded variants introduced:
- The Files utility class became even more useful:
- static String readString(Path path) throws IOException
- static String readString(Path path, Charset cs) throws IOException
- static Path writeString(Path path, CharSequence csq, OpenOption… options) throws IOException
- static Path writeString(Path path, CharSequence csq, Charset cs, OpenOption… options) throws IOException
- In the same vein, the Path class had a few factory methods introduced:
- A new default method was added to Collection interface, complementing other favors:
- The TimeUnit had a new conversion option:
- The PriorityQueue was enhanced with the implementation of:
- Consequently, the PriorityBlockingQueue was enhanced with the implementation of:
- Multiple enhancements went into ByteBuffer support by Deflater:
- … and by Inflater:
It worth to note that JDK-11 had introduced two new garbage collectors, ZGC and Epsilon, both were marked as experimental. We are going to get back to those in the upcoming posts while discussing more recent JDK releases.
So, where are we today? The JDK-11 slowly but steadily getting more adoption as more and more projects migrate off the JDK-8. Nonetheless, the majority are still on JDK-8 and in my opinion, there are no reasons to expect drastic changes of the balance within next couple of years. But this is another story …
Published on Java Code Geeks with permission by Andrey Redko, partner at our JCG program. See the original article here: Chasing Java’s release train, from 8 to 16. Part 2: The race to the next LTS release Opinions expressed by Java Code Geeks contributors are their own. |