17 March 2020 saw the release of JDK 14, per Java's new release cadence of six months. You can download this new JDK from AdoptOpenJDK website. The command to get Java version:
$ java -version openjdk version "14" 2020-03-17 OpenJDK Runtime Environment AdoptOpenJDK (build 14+36) OpenJDK 64-Bit Server VM AdoptOpenJDK (build 14+36, mixed mode, sharing)
In this article, we'll take a look at the new features and improvements introduced in this release.
JEP 305: Pattern Matching for instanceof (Preview)
This is a preview language feature, JEP 305 enhance Java with pattern matching for the instanceof
operator. There is no need for a separate declaration and a cast, as instanceof
can be used with an identifier as a local variable if instanceof
expression is true.
package com.dariawan.jdk14;
public class JEP305 {
public static void main(String[] args) {
Object obj1 = "Dariawan";
// Before JDK 14
if (obj1 instanceof String) {
String str = (String) obj1;
System.out.printf("String: %s\n", str);
} else {
System.out.printf("Not a string\n");
}
// Preview feature in JDK 14
// instanceof, cast and bind variable in one line.
if (obj1 instanceof String str) {
System.out.printf("String: %s\n", str);
} else {
System.out.printf("Not a string\n");
}
Object obj2 = " ";
// work for &&, not for ||
if (obj2 instanceof String str && str.isBlank()) {
System.out.printf("It's a String, and blank");
} else {
System.out.printf("It's not a String or not blank");
}
}
}
More in article: Java 14 - Pattern Matching for instanceof (JEP 305).
JEP 343: Packaging Tool (Incubator)
JEP 343 aims to create a simple packaging tool for self-contained Java applications. This tool, jpackage
is based on the JavaFX javapackager tool that was removed in Oracle JDK 11. We will have more detail about this tool in Java 14 - Creating Self-Contained Java Applications With Packaging Tool (JEP 343).
JEP 345: NUMA-Aware Memory Allocation for G1
Non-uniform Memory Access (NUMA) architecture is common in systems with multiple processors. NUMA is a way of configuring cluster of microprocessor into a multiprocessing system, by assigning each processor is a specific local memory exclusively for its own use. In this setup, a processor can access local memory much faster than non-local memory (which having more latency).
JEP 345 aims to improve G1 performance on large systems by implementing NUMA-aware memory allocation. G1's heap is arranged as a group of fixed-size regions. The regions will be evenly spread across the total number of available NUMA nodes when the JVM is initialized and the +XX:+UseNUMA
option is specified, .
JEP 349: JFR Event Streaming
JDK Flight Recorder(JFR) is a tool for collecting diagnostic and profiling data about the JVM itself, and the application running in the JVM. It is integrated into the JVM and can be used even in heavily loaded production environments with almost no performance overhead.
JEP 349 is enhancements for existing JFR to expose it's data for continuous monitoring. This allowing the streaming of JFR data in real-time, without the need to dump the recorded events to storage and parse it later. So in short, it will open the door for profiling, analysis, or debugging in real time, compared to collection and post collection step as before.
More on article: Java 14 - JFR Event Streaming (JEP 349)
You can find about Java 11 and JFR in this related article: Java 11 - Flight Recorder (JEP 328)
JEP 352: Non-Volatile Mapped Byte Buffers
In JEP 352, FileChannel
API is improved to support creating MappedByteBuffer
instances on non-volatile memory (NVM) or persistent memory. This feature ensures that any changes which might still be in the cache are written back to memory, and only supported in Linux/x64 and Linux/AArch64 platforms.
JEP 358: Helpful NullPointerExceptions
In JEP 358, NullPointerException
s generated by the JVM will give greater usability by precisely pointing out which variable was null
. To enable this feature, add -XX:+ShowCodeDetailsInExceptionMessages
option.
For example, let's take a look again my NullPointerExample
in article Java 8 Optional Overview With Examples. I bring it as JEP358NullPointerExample
, please refer to class Department
and Employee
in the same article (copied into com.dariawan.jdk14.dto
package).
package com.dariawan.jdk14;
import com.dariawan.jdk14.dto.Employee;
import java.time.LocalDate;
import java.time.Month;
public class JEP358NullPointerExample {
public static void main(String[] args) {
Employee emp = new Employee();
emp.setId(1001);
emp.setName("Clark Kent");
emp.setBirthDate(LocalDate.of(1974, Month.JUNE, 18));
System.out.println(emp.getDepartment().getName());
}
}
Running it without -XX:+ShowCodeDetailsInExceptionMessages
option:
$ java --enable-preview com.dariawan.jdk14.JEP358NullPointerExample Exception in thread "main" java.lang.NullPointerException at com.dariawan.jdk14.JEP358NullPointerExample.main(JEP358NullPointerExample.java:53)
That's the details you get before Java 14, and without those option. And now running it with -XX:+ShowCodeDetailsInExceptionMessages
option:
$ java --enable-preview -XX:+ShowCodeDetailsInExceptionMessages com.dariawan.jdk14.JEP358NullPointerExample Exception in thread "main" java.lang.NullPointerException: Cannot invoke "com.dariawan.jdk14.dto.Department.getName()" because the return value of "com.dariawan.jdk14.dto.Employee.getDepartment()" is null at com.dariawan.jdk14.JEP358NullPointerExample.main(JEP358NullPointerExample.java:53)
Check this article: Java 14 - Helpful NullPointerExceptions (JEP 358).
JEP 359: Records (Preview)
To enhance the Java language to compact the class declaration syntax with record
. For a simple class, we need to write a lot of low-value, repetitive code: constructors, setters/getters, equals(), hashCode(), toString(), etc. Yes, there are third party library like lombok helping us with this repetitive "tasks", but record
is another game changer.
For example, let's convert Department
and Employee
as record
s
package com.dariawan.jdk14.records;
public record Department (
Integer id,
String name) {
}
package com.dariawan.jdk14.records;
import java.time.LocalDate;
public record Employee(
Integer id,
String name,
LocalDate birthDate,
Department department) {
}
Then create JEP359Records test class:
package com.dariawan.jdk14;
import com.dariawan.jdk14.records.Employee;
import com.dariawan.jdk14.records.Department;
import java.time.LocalDate;
import java.time.Month;
public class JEP359Records {
public static void main(String[] args) {
Employee emp = new Employee(101, "Janice Leah Abigail",
LocalDate.of(1997, Month.MAY, 7),
new Department(1, "IT - Application"));
System.out.println(emp);
}
}
And when we running it:
$ java --enable-preview com.dariawan.jdk14.JEP359Records Employee[id=101, name=Janice Leah Abigail, birthDate=1997-05-07, department=Department[id=1, name=IT - Application]]
It's very straightforward! More in article: Java 14 - Records Preview Feature (JEP 359).
JEP 361: Switch Expressions (Standard)
Switch Expressions, was a preview language feature in JDK 12 and JDK 13. Based on JEP 361, It's now a standard language feature in JDK 14, means we can use it without a need to specify option --enable-preview
. Please check following tutorials:
JEP 362: Deprecate the Solaris and SPARC Ports
In JEP 362 Solaris/SPARC, Solaris/x64, and Linux/SPARC ports is deprecated and will be removed in a future release.
JEP 363: Remove the Concurrent Mark Sweep (CMS) Garbage Collector
CMS Garbage Collected was deprecated in Java 9, and with JEP 363 — it's removed in Java 14.
$ java -XX:+UseConcMarkSweepGC com.dariawan.jdk14.JEP363 OpenJDK 64-Bit Server VM warning: Ignoring option UseConcMarkSweepGC; support was removed in 14.0 Remove the Concurrent Mark Sweep (CMS) GC
Besides CMS, there are two other GCs: ZGC (since Java 11 — only in Linux/x64) and Shenandoah (since Java 12), and both are still in experimental stage.
JEP 364: ZGC on macOS (Experimental)
ZGC was introduced in Java 11, but it was only supported Linux/X64. JEP 364 bring it to macOS.
JEP 365: ZGC on Windows (Experimental)
Similar for Windows platform, JEP 365 bring ZGC to Windows.
JEP 366: Deprecate the ParallelScavenge + SerialOld GC Combination
Based on JEP 366, the combination of the Parallel Scavenge (called ParallelScavenge) and Serial Old garbage collection (called SerialOld) algorithms is deprecated due to little use and large maintenance effort.
$ java -XX:-UseParallelOldGC com.dariawan.jdk14.JEP366 OpenJDK 64-Bit Server VM warning: Option UseParallelOldGC was deprecated in version 14.0 and will likely be removed in a future release.
JEP 367: Remove the Pack200 Tools and API
The pack200
and unpack200
tools, and the Pack200
API in the java.util.jar
package were deprecated for removal in Java SE 11. And with JEP 367, this is completely removed in JDK 14.
JEP 368: Text Blocks (Second Preview)
This was started as JEP 355, a preview feature in Java 13, and JEP 368 is the second preview for this feature. Text blocks making it easier for you to work with multi line string literals, avoiding the need for most escape sequences. Text blocks also give greater control on how to format your strings and improve the readability of your code.
In this release, there are two new escape sequences:
\
at the end-of-line will suppress the line termination.\s
will resulting a single space.
Check following code:
package com.dariawan.jdk14;
public class JEP368TextBlocks {
public static void main(String[] args) {
String html1 = """
<html>
<body>
<p>Text Blocks as in Java 13</p>
</body>
</html>
""";
System.out.println(html1);
String html2 = """
<html>
<body>\
<p>Text Blocks as in Java\s14</p>\
</body>
</html>
""";
System.out.println(html2);
}
}
And the result is:
$ java --enable-preview com.dariawan.jdk14.JEP368TextBlocks <html> <body> <p>Text Blocks as in Java 13</p> </body> </html> <html> <body> <p>Text Blocks as in Java 14</p> </body> </html>
Also find related posting for JDK 13 release: Java 13 - Text Blocks (JEP 355)
JEP 370: Foreign-Memory Access API (Incubator)
Although until now the Java API does not provide a satisfactory solution for accessing foreign memory, Accessing foreign memory is not something new in JVM world. This is done by many Java libraries and programs, like Ignite, mapDB, memcached, and Netty's ByteBuf API.
JEP 370 introduces an API to allow Java programs to efficiently access native memory segments out of JVM heap space (off-heap). There are three main interfaces:
MemorySegment
: models a contiguous region of memory with given bounds.MemoryAddress
: encodes an offset within a givenMemorySegment
, basically a pointer.MemoryLayout
: used to describe the contents of a memory segment in a language neutral fashion.
And a class that used in our example:
MemoryHandles
: provides several factory methods for constructing and combining memory access var handles.
package com.dariawan.jdk14;
import jdk.incubator.foreign.MemoryHandles;
import jdk.incubator.foreign.MemorySegment;
import jdk.incubator.foreign.MemoryAddress;
import java.lang.invoke.VarHandle;
import java.nio.ByteOrder;
public class JEP370OffHeap {
public static void main(String[] args) {
VarHandle intHandle = MemoryHandles.varHandle(int.class, ByteOrder.nativeOrder());
try (MemorySegment segment = MemorySegment.allocateNative(100)) {
MemoryAddress base = segment.baseAddress();
for (int i = 0; i <= 25; i++) {
var rebase = base.addOffset(i * 4);
// set value i into the foreign memory
intHandle.set(rebase, i);
// print memory address and the value from foreign memory
System.out.printf("%s: %s\n", rebase, intHandle.get(rebase));
}
}
}
}
You need to add module jdk.incubator.foreign
to try this feature. For my case, in Maven's pom.xml I added these arg(s) in maven-compiler-plugin
:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>14</release> <compilerArgs> <arg>--enable-preview</arg> <arg>--add-modules</arg> <arg>jdk.incubator.foreign</arg> </compilerArgs> </configuration> </plugin>
And when running it:
>java --enable-preview --add-modules jdk.incubator.foreign com.dariawan.jdk14.JEP370OffHeap WARNING: Using incubator modules: jdk.incubator.foreign MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x0 }: 0 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x4 }: 1 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x8 }: 2 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0xc }: 3 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x10 }: 4 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x14 }: 5 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x18 }: 6 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x1c }: 7 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x20 }: 8 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x24 }: 9 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x28 }: 10 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x2c }: 11 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x30 }: 12 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x34 }: 13 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x38 }: 14 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x3c }: 15 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x40 }: 16 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x44 }: 17 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x48 }: 18 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x4c }: 19 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x50 }: 20 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x54 }: 21 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x58 }: 22 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x5c }: 23 MemoryAddress{ region: MemorySegment{ id=0x4fa41ae5 limit: 100 } offset=0x60 }: 24 Exception in thread "main" java.lang.IndexOutOfBoundsException: Out of bound access on segment MemorySegment{ id=0x4fa41ae5 limit: 100 }; new offset = 100; new length = 4 at jdk.incubator.foreign/jdk.internal.foreign.MemorySegmentImpl.checkBounds(MemorySegmentImpl.java:199) at jdk.incubator.foreign/jdk.internal.foreign.MemorySegmentImpl.checkRange(MemorySegmentImpl.java:178) at jdk.incubator.foreign/jdk.internal.foreign.MemoryAddressImpl.checkAccess(MemoryAddressImpl.java:84) at java.base/java.lang.invoke.VarHandleMemoryAddressAsInts.checkAddress(VarHandleMemoryAddressAsInts.java:50) at java.base/java.lang.invoke.VarHandleMemoryAddressAsInts.set0(VarHandleMemoryAddressAsInts.java:85) at java.base/java.lang.invoke.VarHandleMemoryAddressAsInts0/0x0000000800ba5840.set(Unknown Source) at java.base/java.lang.invoke.VarHandleGuards.guard_LI_V(VarHandleGuards.java:114) at com.dariawan.jdk14.JEP370OffHeap.main(JEP370OffHeap.java:53)
The IndexOutOfBoundsException
happen as expected because we just allocated 100 in MemorySegment.allocateNative(100)
More in Package jdk.incubator.foreign.