Java 9 Is Here!

Public Service Announcement

  • new release schedule

  • Oracle JDK ≠ OpenJDK

  • Java on the desktop

New Release Schedule

  • starting with Java 9,
    new major release every six months

  • Java 10 comes out today!

  • Java 11 comes out in September

Oracle JDK ≠ OpenJDK

  • no free Oracle JDK from Java 11 on

  • Oracle ships OpenJDK builds

  • but only for current major version:
    ⇝ 10 ⇝ 10.0.1 ⇝ 10.0.4
    ⇝ 11 ⇝ 11.0.1 ⇝ 11.0.4
    ⇝ 12 ⇝ 12.0.1 ⇝ 12.0.4

  • there is no spoon free LTS by Oracle!

⇝ More in my weekly:
No Free Java LTS Version?

Java on the desktop

Oracle released a
Java Client Roadmap Update
with plans for:

  • Applets

  • Web Start

  • JavaFX

  • Swing/AWT

Applets

  • Applets are deprecated in Java 9

  • Applets are removed from Java SE 11

  • Applets won’t be in Oracle’s OpenJDK 11 builds

Web Start

  • Web Start is deprecated in Java 9

  • Web Start is removed from Java SE 11

  • Web Start won’t be in Oracle’s OpenJDK 11 builds

JavaFX

  • was never part of Java SE
    (unlikely to change soon)

  • was never in Oracle’s OpenJDK builds
    (just as unlikely to change)

  • will be removed from Oracle JDK 11

If your project uses JavaFX,
you can’t rely on its presence
on your customers' machines!

JavaFX

Quote:

Oracle is working with interested third parties to make it easier to build and maintain JavaFX as a separately distributable open-source module.

Not a quote:

Oracle wants to reduce/terminate investment in JavaFX.

Swing/AWT

Quote:

Oracle will continue developing Swing and AWT in Java SE 8 and Java SE 11 (18.9 LTS). This means they will be supported by Oracle through at least 2026.

What this doesn’t say:

  • support is free

  • support goes beyond Java 11

Swing/AWT

Hypothetically Oracle could
remove Swing/AWT from Java SE 12
and its statement still holds.

⇝ More in my weekly:
Java Desktop, Quo Vadis?

More PSAs

To hear about such news:

Let’s get started!

  • some features come with practical exercises

  • others are just showcased

  • slides at slides.codefx.org

Lots to talk about!

Language Changes
New and Updated APIs
New JVM Features
Performance Improvements

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

Enabling reuse between default methods.

No Reuse

public interface InJava8 {

	default boolean evenSum(int... numbers) {
		return sum(numbers) % 2 == 0;
	}

	default boolean oddSum(int... numbers) {
		return sum(numbers) % 2 == 1;
	}

	default int sum(int[] numbers) {
		return IntStream.of(numbers).sum();
	}

}

Private Interface Methods

public interface InJava9 {

	private int sum(int[] numbers) {
		return IntStream.of(numbers).sum();
	}

}

Just like private methods in abstract classes:

  • must be implemented

  • can not be overriden

  • can only be called in same source file

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

Making try-with-resources blocks cleaner.

Useless Variable

void doSomethingWith(Connection connection)
		throws Exception {
	try(Connection c = connection) {
		c.doSomething();
	}
}

Why is c necessary?

Why is c necessary?

  • target of close() must be obvious
    ⇝ resource should not be reassigned

  • easiest if resource is final

  • easiest if resource must be assigned
    and can be made implicitly final

try(Connection c = connection)

Effectively Final Resource

But since Java 8 we have effectively final!

This works in Java 9:

void doSomethingWith(Connection connection)
		throws Exception {
	try(connection) {
		connection.doSomething();
	}
}
  • compiler knows that connection is not reassigned

  • developers know what effectively final means

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

A little more type inference.

Diamond Operator

Maybe the best example:

List<String> strings = new ArrayList<>();
  • used at a constructor call

  • tells Java to infer the parametric type

Anonymous Classes

Diamond did not work with anon. classes:

<T> Box<T> createBox(T content) {
	// we have to put the `T` here :(
	return new Box<T>(content) { };
}

Reason are non-denotable types:

  • might be inferred by compiler for anon. classes

  • can not be expressed by JVM

Infer Denotable Types

Java 9 infers denotable types:

<T> Box<T> createBox(T content) {
	return new Box<>(content) { };
}

Gives compile error if type is non-denotable:

Box<?> createCrazyBox(Object content) {
	List<?> innerList = Arrays.asList(content);
	// compile error
	return new Box<>(innerList) { };
}

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

One less warning you couldn’t do anything about.

Heap Pollution

Innocent looking code…​

private <T> Optional<T> firstNonNull(T... args) {
	return stream(args)
			.filter(Objects::nonNull)
			.findFirst();
}

Compiler warns (on call site, too):

Possible heap pollution from parameterized vararg type

Heap Pollution?

For generic varargs argument T…​ args,
you must not depend on it being a T[]!

private <T> T[] replaceTwoNulls(
		T value, T first, T second) {
	return replaceAllNulls(value, first, second);
}

private <T> T[] replaceAllNulls(T value, T... args) {
	// loop over `args`, replacing `null` with `value`
	return args;
}

Compiler Warning

Compiler is aware of the problem and warns you.

If you think, everything’s under control:

@SafeVarargs
private <T> Optional<T> firstNonNull(T... args) {
	return // [...]
}

Or not…​ In Java 8 this is a compile error!

Invalid SafeVarargs annotation. Instance
method <T>firstNonNull(T...) is not final.

But Why?

The @SafeVarargs annotation:

  • tells the caller that all is fine

  • only makes sense on methods
    that can not be overriden

Which methods can’t be overriden?
final methods

What about private methods?
⇝ Damn! 😭

@SafeVarargs on Private Methods

Looong story, here’s the point:

In Java 9 @SafeVarargs
can be applied to private methods.

New Language Features

Private Interface Methods
Try-With-Resources
Diamond Operator
SafeVarargs
Deprecation Warnings

Another warning you couldn’t do anything about.

Deprecation Warnings

Should this code emit a warning?

// LineNumberInputStream is deprecated
import java.io.LineNumberInputStream;


public class DeprecatedImports {

    LineNumberInputStream stream;

}
// LineNumberInputStream is deprecated
import java.io.LineNumberInputStream;

@Deprecated
public class DeprecatedImports {

    LineNumberInputStream stream;

}

Not On Imports

Java 9 no longer emits warnings
for importing deprecated members.

Warning free:

import java.io.LineNumberInputStream;

@Deprecated
public class DeprecatedImports {

	LineNumberInputStream stream;

}

New or Updated APIs

Stream Improvements
Optional Improvements
Collection Factories
Stack-Walking
OS Processes
Reactive Streams

Some APIs are new,
many existing ones were improved.

Stream Improvements

Stream Improvements
Optional Improvements
Collection Factories
Stack-Walking
OS Processes
Reactive Streams

Small improvements to Java 8 streams.

Stream::ofNullable

Creates a stream of zero or one elements:

long zero = Stream.ofNullable(null).count();
long one = Stream.ofNullable("42").count();

Stream::iterate

To use for even less…​

iterate(T seed,
	Predicate<T> hasNext,
	UnaryOperator<T> next);

Example:

Stream
	.iterate(1, i -> i<=10, i -> 2*i)
	.forEach(System.out::println);
// output: 1 2 4 8

Stream::iterate

Counter Example:

Enumeration<Integer> en = // ...
Stream.iterate(
		en.nextElement(),
		el -> en.hasMoreElements(),
		el -> en.nextElement())
	.forEach(System.out::println);
  • first nextElement()

  • then hasMoreElements()

  • ⇝ fail

Stream::takeWhile

Stream as long as a condition is true:

Stream<T> takeWhile(Predicate<T> predicate);

Example:

Stream.of("a-", "b-", "c-", "", "e-")
	.takeWhile(s -> !s.isEmpty());
	.forEach(System.out::print);

// output: a-b-c-

Stream::dropWhile

Ignore as long as a condition is true:

Stream<T> dropWhile(Predicate<T> predicate);

Example:

Stream.of("a-", "b-", "c-", "de-", "f-")
	.dropWhile(s -> s.length() <= 2);
	.forEach(System.out::print);

// output: de-f-

Optional Improvements

Stream Improvements
Optional Improvements
Collection Factories
Stack-Walking
OS Processes
Reactive Streams

Small improvements to Java 8 Optional.

Optional::stream

Turns an Optional into a Stream
of zero or one elements:

Stream<T> stream();

Filter-Map …​

private Optional<Customer> findCustomer(String id) {
	// ...
}

Stream<Customer> findCustomers(List<String> ids) {
	return ids.stream()
		.map(this::findCustomer)
		// now we have a Stream<Optional<Customer>>
		.filter(Optional::isPresent)
		.map(Optional::get)
}

…​ in one Step

private Optional<Customer> findCustomer(String id) {
	// ...
}

Stream<Customer> findCustomers(List<String> ids) {
	return ids.stream()
		.map(this::findCustomer)
		// now we have a Stream<Optional<Customer>>
		// we can now filter-map in one step
		.flatMap(Optional::stream)
}

From Eager to Lazy

List<Order> getOrders(Customer c) is expensive:

List<Order> findOrdersForCustomer(String id) {
	return findCustomer(id)
		.map(this::getOrders) // eager
		.orElse(new ArrayList<>());
}

Stream<Order> findOrdersForCustomer(String id) {
	return findCustomer(id)
		.stream()
		.map(this::getOrders) // lazy
		.flatMap(List::stream);
}

Optional::or

Choose a non-empty Optional:

Optional<T> or(Supplier<Optional<T>> supplier);

Find in Many Places

public interface Search {
	Optional<Customer> inMemory(String id);
	Optional<Customer> onDisk(String id);
	Optional<Customer> remotely(String id);

	default Optional<Customer> anywhere(String id) {
		return inMemory(id)
			.or(() -> onDisk(id))
			.or(() -> remotely(id));
	}

}

ifPresentOrElse

Like ifPresent but do something if empty:

void ifPresentOrElse(
	Consumer<T> action,
	Runnable emptyAction);

Example:

void logLogin(String id) {
	findCustomer(id)
		.ifPresentOrElse(
			this::logCustomerLogin,
			() -> logUnknownLogin(id));
}

Collection Factories

Stream Improvements
Optional Improvements
Collection Factories
Stack-Walking
OS Processes
Reactive Streams

Creating ad-hoc collections more easily.

Hope is Pain

Wouldn’t this be awesome?

List<String> list = [ "a", "b", "c" ];
Map<String, Integer> map = [ "one" = 1, "two" = 2 ];

Not gonna happen!

  • language change is costly

  • binds language to collection framework

  • strongly favors specific collections

Next Best Thing

List<String> list = List.of("a", "b", "c");
Map<String, Integer> mapImmediate = Map.of(
		"one", 1,
		"two", 2,
		"three", 3);
Map<String, Integer> mapEntries = Map.ofEntries(
		entry("one", 1),
		entry("two", 2),
		entry("three", 3));

Interesting Details

  • collections are immutable
    (no immutability in type system, though)

  • collections are value-based

  • null elements/keys/values are forbidden

  • iteration order is random between JVM starts
    (except for lists, of course!)

Stack-Walking

Stream Improvements
Optional Improvements
Collection Factories
Stack-Walking
OS Processes
Reactive Streams

Examining the stack faster and easier.

StackWalker::forEach

void forEach (Consumer<StackFrame>);
public static void main(String[] args) { one(); }
static void one() { two(); }
static void two() {
	StackWalker.getInstance()
		.forEach(System.out::println);
}

// output
StackWalkingExample.two(StackWalking.java:14)
StackWalkingExample.one(StackWalking.java:11)
StackWalkingExample.main(StackWalking.java:10)

StackWalker::walk

T walk (Function<Stream<StackFrame>, T>);
static void three() {
	String line = StackWalker.getInstance().walk(
		frames -> frames
			.filter(f -> f.getMethodName().contains("one"))
			.findFirst()
			.map(f -> "Line " + f.getLineNumber())
			.orElse("Unknown line");
	);
	System.out.println(line);
}

// output
Line 11

Options

getInstance takes options as arguments:

  • SHOW_REFLECT_FRAMES for reflection frames

  • SHOW_HIDDEN_FRAMES e.g. for lambda frames

  • RETAIN_CLASS_REFERENCE for Class<?>

Frames and Traces

forEach and walk operate on StackFrame:

  • class and method name

  • class as Class<?>

  • bytecode index and isNative

Can upgrade to StackTraceElement (expensive):

  • file name and line number

Performance I

stack walker vs exception

Performance II

stack walker limit with estimated size

Performance III

  • creating StackTraceElement is expensive
    (for file name and line number)

  • lazy evaluation pays off for partial traversal

(Benchmarks performed by Arnaud Roger)

OS Processes

Stream Improvements
Optional Improvements
Collection Factories
Stack-Walking
OS Processes
Reactive Streams

Improving interaction with OS processes.

Simple Example

ls /home/nipa/tmp | grep pdf
Path dir = Paths.get("/home/nipa/tmp");
ProcessBuilder ls = new ProcessBuilder()
		.command("ls")
		.directory(dir.toFile());
ProcessBuilder grepPdf = new ProcessBuilder()
		.command("grep", "pdf")
		.redirectOutput(Redirect.INHERIT);
List<Process> lsThenGrep = ProcessBuilder
		.startPipeline(asList(ls, grepPdf));

Extended Process

Cool new methods on Process:

  • boolean supportsNormalTermination();

  • long pid();

  • CompletableFuture<Process> onExit();

  • Stream<ProcessHandle> children();

  • Stream<ProcessHandle> descendants();

  • ProcessHandle toHandle();

New ProcessHandle

New functionality actually comes from ProcessHandle.

Interesting static methods:

  • Stream<ProcessHandle> allProcesses();

  • Optional<ProcessHandle> of(long pid);

  • ProcessHandle current();

More Information

ProcessHandle can return Info:

  • command, arguments

  • start time

  • CPU time

Reactive Streams

Stream Improvements
Optional Improvements
Collection Factories
Stack-Walking
OS Processes
Reactive Streams

The JDK as common ground
for reactive stream libraries.

Reactive Types

Publisher
  • produces items to consume

  • can be subscribed to

Subscriber
  • subscribes to publisher

  • onNext, onError, onComplete

Subscription
  • connection between publisher and subscriber

  • request, cancel

Reactive Flow

Subscribing

  • create Publisher pub and Subscriber sub

  • call pub.subscribe(sub)

  • pub creates Subscription script
    and calls sub.onSubscription(script)

  • sub can store script

Reactive Flow

Streaming

  • sub calls script.request(10)

  • pub calls sub.onNext(element) (max 10x)

Canceling

  • pub may call sub.OnError(err)
    or sub.onComplete()

  • sub may call script.cancel()

Reactive APIs?

JDK only provides three interfaces
and one simple implementation.

(Also called Flow API.)

No JDK API uses them.
(No reactive HTTP connections etc.)

A Mixed Bag Of API Changes

Many lower-level APIs.

JVM Features

Multi-Release JARs
Redirected Platform Logging

Multi-Release JARs

Multi-Release JARs
Redirected Platform Logging

"Do this on Java X, do that on Java Y."

Version Dependence

Main calls Version:

public class Main {

	public static void main(String[] args) {
		System.out.println(new Version().get());
	}

}

Version Dependence

Version exists twice:

public class Version {

	public String get() { return "Java 8"; }

}

public class Version {

	public String get() { return "Java 9"; }

}

(Btw, IDEs hate this!)

Creating A Multi‑Release JAR

Now, here’s the magic:

  • compile Main and Version[8] to out/java-8

  • compile Version[9] to out/java-9

  • use new jar flag --release:

    jar --create --file out/mr.jar
    	-C out/java-8 .
    	--release 9 -C out/java-9 .

JAR Content

└ org
    └ codefx ... (moar folders)
        ├ Main.class
        └ Version.class
└ META-INF
    └ versions
        └ 9
            └ org
                └ codefx ... (moar folders)
                    └ Version.class

Run!

With java -cp out/mr.jar …​Main:

  • prints "Java 8" on Java 8

  • prints "Java 9" on Java 9

Great Success!

Redirected Platform Logging

Multi-Release JARs
Redirected Platform Logging

Use your logging framework of choice
as backend for JDK logging.

Loggers and Finders

New logging infrastructure inside the JDK:

  • new interface System.Logger

  • used by JDK classes

  • instances created by System.LoggerFinder

The interesting bit:

LoggerFinder is a service!

Creating a Logger

public class SystemOutLogger implements Logger {

	public String getName() { return "SystemOut"; }

	public boolean isLoggable(Level level) { return true; }

	public void log(
			Level level, ResourceBundle bundle,
			String format, Object... params) {
		System.out.println(/* ...*/);
	}

	// another, similar `log` method

}

Creating a LoggerFinder

public class SystemOutLoggerFinder
		extends LoggerFinder {

	public Logger getLogger(
			String name, Module module) {
		return new SystemOutLogger();
	}

}

Registering the Service

Module descriptor for system.out.logger:

module system.out.logger {
    provides java.lang.System.LoggerFinder
        with system.out.logger.SystemOutLoggerFinder;
}

Module system and JDK take care of the rest!

A Mixed Bag Of New JVM Features

Performance

Compact Strings
Indified String Concatenation

Compact Strings

Compact Strings
Indified String Concatenation

Going from UTF-16 to ISO-8859-1.

Strings and memory

  • 20% - 30% of heap are char[] for String

  • a char is UTF-16 code unit ⇝ 2 bytes

  • most strings only require ISO-8859-1 ⇝ 1 byte

10% - 15% of memory is wasted!

Compact Strings

For Java 9, String was changed:

  • uses byte[] instead of char[]

  • bytes per character:

    • 1 if all characters are ISO-8859-1

    • 2 otherwise

Only possible because String makes
defensive copies of all arguments.

Performance

Simple benchmark:
(by Aleksey Shipilëv)

String method = generateString(size);

public String work() {
	return "Calling method \"" + method + "\"";
}

Depending on circumstances:

  • throughput 1.4x

  • garbage less 1.85x

Indified String Concatenation

Compact Strings
Indified String Concatenation

"Improving" + "String" + "Concatenation"

String Concatenation

What happens when you run:

String s = greeting + ", " + place + "!";
  • bytecode uses StringBuilder

  • JIT may (!) recognize and optimize
    by writing content directly to new byte[]

  • breaks down quickly
    (e.g. with long or double)

Why Not Create Better Bytecode?

  • new optimizations create new bytecode

  • new optimizations require recompile

  • test matrix JVMs vs bytecodes explodes

Why Not Call String::concat?

There is no such method.

  • concat(String…​ args) requires toString

  • concat(Object…​ args) requires boxing

Nothing fancy can be done
because compiler must use public API.

Invokedynamic

Invokedynamic came in Java 7:

  • compiler creates a recipe

  • runtime has to process it

  • defers decisions from compiler to runtime

(Used for lambda expressions and in Nashorn.)

Indy To The Rescue

With Indy compiler can express
"concat these things"
(without boxing!)

JVM executes by writing content
directly to new byte[].

Performance

Depending on circumstances:

  • throughput 2.6x

  • garbage less 3.4x

(Benchmarks by Aleksey Shipilëv)

Performance Of Indified Compact String Concat

Depending on circumstances:

  • throughput 2.9x

  • garbage less 6.4x

(Benchmarks by Aleksey Shipilëv)

A Mixed Bag Of Performance Improvements

  • cgroup-memory limits

  • something with interned strings
    and class data sharing (JEP 250)

  • contended locks (JEP 143)

  • security manager (JEP 232)

  • GHASH/RSA computation (JEP 246)

  • Java 2D rendering (JEP 265)

Full Picture

If you want the full picture:

If you run into problems:

About Nicolai Parlog

37% off with
code fccparlog

tiny.cc/jms

Follow

Want More?

⇜ Get my book!

You can hire me:

  • training (Java 8-11, JUnit 5)

  • consulting (Java 8-11)

Image Credits