Definitive Guide To Java 13

A detailed guide to Java 13: text blocks, switch expressions with yield, ZGC, dynamic AppCDS archives

Java 13 was released an hour ago and here's everything you need to know about it, starting with migration considerations and version requirements before getting into the juicy bits: First and foremost the new text blocks, but there are also a few smaller additions like a refinement of switch expressions and a usability improvement for application class-data sharing. Here's your overview with lots of links to more detailed articles.

Migrating to Java 13

I will assume, you're at least on Java 11. If you're not, don't feel bad about it (few projects are), but I recommend to try and change that. Start by reading my Java 11 migration guide and about these lesser known gems in Java 11.

Migrating from Java 11?

If you're on 11, it is less straightforward.

If you're on Java 12, you will update to 13.

So, assuming you're on Java 11 or later, the question is whether you will update to Java 13. If you're already on 12, the answer is easy: Yes, you will. Java 12 is no longer supported and will not see any security fixes.

If you're on 11 (everybody's favorite LTS), it is much harder to find an appropriate answer. I discussed this in detail in my Java 12 guide and I won't repeat it all here. Just as a rule of thumb, the more of the following properties your project has, the more I'd play it safe by staying on 11:

  • runs at customer site on machines you have no control over
  • not containerized, may share JVM with other processes
  • the project has lots of dependencies
  • it is hard to keep dependencies up to date
  • the migrations from 9 to 10 and 10 to 11 were not trivial (removal of Java EE modules aside)

Version Requirements

Here are the most common IDEs' and build tools' minimum version requirements for Java 13 (although I advise to always pick the newest available version just to be safe):

  • IntelliJ IDEA: 2019.2
  • Eclipse: 2019-09 (4.13) with Java 13 Support plugin
  • Maven: generally speaking 3.5.0, but e.g. this bug was only fixed in 3.6.1
    • compiler plugin: 3.8.0
    • surefire and failsafe: 2.22.0
    • plugins using ASM (e.g. the shade plugin) will likely need to be updated as well
  • Gradle: 6.0

When it comes to compiling to Java 13 bytecode, keep in mind that you will likely have to update all dependencies that rely on bytecode manipulation, e.g. Spring, Hibernate, Mockito, etc.

Preview Features

I wrote a dedicated post on preview features, so I will stick to the very basics here. If you want to use text blocks or switch expressions (see below for details on them), include this in your build tool configuration:

<!-- Maven's pom.xml -->
// Gradle's build.gradle
compileJava {
	options.compilerArgs += ["--enable-preview"]
test {
	jvmArgs '--enable-preview'

In IntelliJ IDEA, set the language level for your module to 13 (Preview). In Eclipse, find the Java Compiler configuration and check Enable preview features.

Language Features

Here's the new syntax you get if you upgrade to Java 13.

Text Blocks

Putting strings that span several lines into code has been a pain in Java since its inception. Now, 20-something years later, we finally get easy-to-use multiline strings, called text blocks:

// ugh!
String jsonLiteral = ""
	+ "{\n"
	+ "\tgreeting: \"Hello\",\n"
	+ "\taudience: \"World\",\n"
	+ "\tpunctuation: \"!\"\n"
	+ "}\n";
// yay! 🎉
String jsonBlock = """
		greeting: "Hello",
		audience: "World",
		punctuation: "!"

Text blocks start with three quotation marks and a newline (the opening delimiter; the newline is not part of the result) and end with three quotation marks either in the last line of content or on its own line (if the latter, the string ends with a newline). They are accepted in the exact same places where string literals "like this one" are. In fact, after the compiler processed them, they are absolutely indistinguishable from strings created by literals. That means jsonLiteral.equals(jsonBlock) is true and, thanks to String interning, even jsonLiteral == jsonBlock is.

The compiler considers indentation that's shared across all lines incidental and removes it. Other leading whitespace is considered essential and left in place. For jsonBlock that means that the lines with { and } have no indentation, but the property lines do. There's lots more to indentation, which I explain in my deep dive into text blocks.

In Java 13, text blocks are a preview feature.

Definite Guide To Text Blocks In Java 13

Programmer's Guide To Text Blocks (by Oracle)

JEP 355: Text Blocks

Returning Values From Switch Expressions

Switch expressions were introduced in Java 12 and 13 refines them. In 12 you would define return values with break:

boolean result = switch (Bool.random()) {
	case TRUE -> {
		System.out.println("Bool true");
		break true;
	case FALSE -> {
		System.out.println("Bool false");
		break false;
	case FILE_NOT_FOUND -> {
		throw new UncheckedIOException(
			"This is ridiculous!",
			new FileNotFoundException());

In 13, you need to use yield:

boolean result = switch (Bool.random()) {
	case TRUE -> {
		System.out.println("Bool true");
		// `yield` instead of `break`
		yield true;
	case FALSE -> {
		System.out.println("Bool false");
		yield false;
	case FILE_NOT_FOUND -> {
		throw new UncheckedIOException(
			"This is ridiculous!",
			new FileNotFoundException());

I slightly prefer the new variant. While break was easy to adopt for developers already familiar with Java, it was pretty odd. I mean, what is break true trying to tell me? The new (conditional) keyword yield is clearer, and in the future it may show up in other places where values are returned - in fact, Brian Goetz said it would have been used for lambdas if they had thought of the necessity back then.

Due to this change, switch expressions are still in preview.

Definitive Guide To Switch Expressions In Java 13

First Contact with Switch Expressions in Java 12 (video)

JEP 325: Switch Expressions (introduction in 12)

JEP 354: Switch Expressions (refinement in 13)

API Improvements

This is usually the place where I turn to new and updated APIs and tell you about all the small tidbits that will make your life easier. Except... there aren't very many in 13. Sad.

JDK 12 to 13 API Change Report

Auto-generated release notes

New String Methods

String got three new methods:

  • String::stripIndent behaves just like the algorithm the compiler uses to remove indentation of text blocks.
  • Similarly, String::translateEscapes exposes the compiler's behavior when translating escape sequences in strings.
  • String::formatted is an instance method reimplementing the static method String::format.

That means calling "Value: %s".formatted(value) is equivalent to String.format("Value: %s", value), but a little more convenient.

NIO Improvements

Then there are a few small improvements in the NIO APIs, but I won't go into detail here - I recommend to check the issues and JavaDoc if the title appear interesting to you:

JDK-5029431: Add absolute bulk put and get methods

JDK-8218418: Files.createSymbolicLink should use SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE

JDK-8215467: Files.isHidden should return true for hidden directories on Windows

JDK-8218875: Add FileSystems.newFileSystem(Path, Map) method

Beyond these small additions, I got a big refactoring for you, though.

Socket And ServerSocket Reimplementation

Project Loom will introduce fibers (light-weight threads managed by the JVM) and a part of that is to have all code that blocks take the same paths (because those paths are then changed to no longer block threads). A critical part of the JDK where threads are blocked are the und classes. Their implementation was very old and didn't line up with Loom's approach, so in preparation of future changes, this API was reimplemented. This should not be noticeable to us.

Project Loom

JEP 353: Reimplement the Legacy Socket API

New JVM Capabilities

As usual, the JVM sees a fair share if improvements. Here are the most important ones.

JDK-8221431: Support for Unicode 12.1

JDK 13 Security Enhancements (by Oracle's Sean Mullan)

Creating Class-Data Archives For AppCDS

Application class-data sharing (AppCDS) was made freely available in Java 10 and improved in 12 and 13. It reduces launch times (by 10% to almost 50%) and response time outliers by moving much of the class-loading work out of the program run. Instead of loading class data from JARs when it's needed, AppCDS prepares an immutable archive file and maps it into memory when the JVM launches. (Or "the JVMs" because the archive can be shared between multiple instances.)

On Java 10, using an archive used to be a three-step process:

  • creating a list of classes to archive
  • creating the archive
  • launching with the archive

Java 12 relaxed this a little by introducing a default archive of JDK classes that is shipped with the JVM and used automatically. But you still had to go through the steps above to create an archive that includes your application classes. This is where Java 13 comes into play.

The new option -XX:ArchiveClassesAtExit tells the JVM to run as usual, but on exit (if it didn't crash), to write the class-data into the specified file. That can then be used on future program launches:

# run without CDS & create archive
	-jar target/java-x.jar

# use created archive
	-jar target/java-x.jar

This approach uses so-called dynamic CDS archives and makes the feature much easier to use. If you care about launch times, give it a shot!

Improve Launch Times On Java 13 With Application Class-Data Sharing

JEP 310: Application Class-Data Sharing

JEP 341: Default CDS Archives

JEP 350: Dynamic CDS Archives

ZGC's Use Of Memory

Oracle's Z Garbage Collector (ZGC) is a scalable low latency garbage collector designed to meet pause times that are independent of heap or live-set size (ranging from a few hundred MB to many TB) and stay below 10 ms. In Java 13, heaps size can be 16 TB and ZGC can return unused memory to the operating system. The command line argument -XX:ZUncommitDelay=<seconds> can be used to configure when that happens.

Then there's a new command line flag -XX:SoftMaxHeapSize that informs the garbage collector to try and limit the heap to the specified size. If it would otherwise run out of memory, it is allowed to use more memory, though, up wo what's specified with -Xmx. This should work well together with returning unused memory.

JEP 351: ZGC: Uncommit Unused Memory

JDK-8221786: ZGC: Increase max heap size to 16TB

JDK-8222145: Add -XX:SoftMaxHeapSize flag

Major improvements for Shenandoah

Another very interesting garbage collector that sees constant improvement is Red Hat's Shenandoah, a low-pause GC that performs most garbage collection work concurrently with the running program. This includes concurrent compaction, which means Shenandoah's pause times are not directly proportional to the size of the heap.

A change in Java 13 is the new load-reference barriers scheme, which offers a much simpler internal implementation, opening up more opportunities for optimizations, which in turn improves performance. One of those optimizations is the removal of separate forwarding pointer word per object, which is supposed to considerably drop the footprint overhead.

Shenandoah also extended its platform support and now runs on all x86_32 platforms plus a few build fixes allows it to be built and used on Solaris x86_64. There are many convenience improvements as well, like changing the default GC threads count, accepting even smaller heap sizes, and better interaction with uncommits.

JDK-8221766: Load-reference barriers

JDK-8222185: Report "committed" as capacity

JDK-8222186: Don't uncommit below minimum heap size

JDK-8223759: Allow arbitrarily low initial heap size

JDK-8223767: Build on Solaris x86_64

JDK-8224584: Eliminate forwarding pointer word

JDK-8225048: x86_32 support

JDK-8225229: Trim down default number of GC threads


And that's Java 13 in a nutshell for you:

  • text blocks (preview)
  • yield for switch expressions (preview)
  • dynamic AppCDS archives with -XX:ArchiveClassesAtExit

Now, go forth, download, and have fun!