Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Developer Advocate

Java Team at Oracle

Let’s get started!

Lots to talk about!

Pattern Matching
Virtual Threads
String Templates
New & Updated APIs
Performance & More

Why upgrade?

Costs of running on old versions:

  • support contract for Java

  • waning support in libraries / frameworks

Why upgrade?

Costs of not running on new versions:

  • lower productivity

  • less observability and performance
    (more on that later)

  • less access to talent

  • bigger upgrade costs

Why upgrade?

Resistence is futile.

How to upgrade?

Preparations:

  • stick to supported APIs

  • stick to standardized behavior

  • stick to well-maintained projects

  • keep dependencies and tools up to date

  • stay ahead of removals (jdeprscan)

  • build on many JDK versions

How to upgrade?

Prepare by building on multiple JDK versions:

  • your baseline version

  • every supported version since then

  • latest version

  • EA build of next version

How to upgrade?

It’s not necessary to build โ€ฆ

  • โ€ฆ each commit on all versions

  • โ€ฆ the whole project on all versions

Build as much as feasible.

What about LTS?

Within OpenJDK, there is no LTS.

โ‡ has no impact on features, reliability, etc.

It’s a vendor-centric concept
to offer continuous fixes
(usually for money).

You’re paying not to get new features.

What about LTS?

inside java newscast 52

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
A New Dynamic Dispatch
Data-Oriented Programming
Virtual Threads
String Templates
New & Updated APIs
Performance & More

A simple app

Features:

  • scrapes GitHub projects

  • creates Page instances:

    • GitHubIssuePage

    • GitHubPrPage

    • ExternalPage

    • ErrorPage

  • further processes pages

A simple app

Features:

  • display as interactive graph

  • compute graph properties

  • categorize pages by topic

  • analyze mood of interactions

  • process payment for analyses

  • etc.

A simple architecture?

How to implement features?

  • methods on Page ๐Ÿ˜ง

  • visitor pattern ๐Ÿ˜ซ

  • pattern matching ๐Ÿฅณ

Pattern Matching

Ingredients:

  • switch expressions โ‘ญ (JEP 361)

  • type pattern matching โ‘ฏ (JEP 394)

  • sealed types โ‘ฐ (JEP 409)

  • patterns in switch ใ‰‘ (JEP 441)

Approach:

  • make Page sealed

  • implement features as methods outside of Page

  • accept Page parameters and switch over it

Sealed Page

Sealed types limit inheritance,
by only allowing specific subtypes.

public sealed interface Page
	permits GitHubIssuePage, GitHubPrPage,
			ExternalPage, ErrorPage {
	// ...
}

Switch over Page

public void categorize(Page page) {
	switch (page) {
		case GitHubIssuePage issue
			-> categorizeIssue(issue);
		case GitHubPrPage pr
			-> categorizePr(pr);
		case ExternalPage external
			-> categorizeExternal(external);
		case ErrorPage error
			-> categorizeError(error);
	}
}

Maintainability

Unlike an if-else-if-chain,
a pattern switch needs to be exhaustive.

Fulfilled by:

  • switching over a sealed types

  • a case per subtype

  • avoiding the default branch

โ‡ Adding a new subtype causes compile error!

Dynamic dispatch

Dynamic dispatch selects the invoked method by type.

As language feature:

  • via inheritance

  • makes method part of API

What if methods shouldn’t be part of the API?

Dynamic dispatch

Without methods becoming part of the API.

Via visitor pattern:

  • makes "visitation" part of API

  • cumbersome and indirect

Via pattern matching (new):

  • makes "sealed" part of type

  • straight-forward

More

More on pattern matching:

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
A New Dynamic Dispatch
Data-Oriented Programming
Virtual Threads
String Templates
New & Updated APIs
Performance & More

Data-Oriented Programming

Use Java’s strong typing to model data as data:

  • use types to model data, particularly:

    • data as data with records

    • alternatives with sealed types

  • use (static) methods to model behavior, particularly:

    • exhaustive switch without default

    • pattern matching to destructure polymorphic data

Data-Oriented Programmingโ€ฆ

โ€ฆ isn’t funtional programming
  • but it’s similar (data + functions)

  • first priority is data, not functions

โ€ฆ doesn’t kill object-oriented programming
  • use OOP to modularize large systems

  • use DOP to model small, data-focused (sub)systems

More

More on data-oriented programming:

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
Unlimited Threads
Under The Hood
String Templates
New & Updated APIs
Performance & More

A simple web request

Imagine a hypothetical HTTP request:

  1. interpret request

  2. query database (blocks)

  3. process data for response

Resource utilization:

  • good for 1. and 3.

  • really bad for 2.

How to implement that request?

Synchronous

Align application’s unit of concurrency (request)
with Java’s unit of concurrency (thread):

  • use thread per request

  • simple to write, debug, profile

  • blocks threads on certain calls

  • limited number of platform threads
    โ‡ bad resource utilization
    โ‡ low throughput

Asynchronous

Only use threads for actual computations:

  • use non-blocking APIs (futures / reactive streams)

  • harder to write, challenging to debug/profile

  • incompatible with synchronous code

  • shares platform threads
    โ‡ great resource utilization
    โ‡ high throughput

Conflict!

There’s a conflict between:

  • simplicity

  • throughput

Enter virtual threads!

A virtual thread:

  • is a regular Thread

  • low memory footprint ([k]bytes)

  • small switching cost

  • scheduled by the Java runtime

  • requires no OS thread when waiting

Think of them like you
think about virtual memory.

Example

try (var executor = Executors
		.newVirtualThreadPerTaskExecutor()) {
	for (int i = 0; i < 1_000_000; i++) {
		var number = i;
		executor.submit(() -> {
			Thread.sleep(Duration.ofSeconds(1));
			return number;
		});
	}
} // executor.close() is called implicitly, and waits

Example

This laptop:

  • Intel i7-1165G7 (11th Gen)

  • 8GB for JVM (32 GB total RAM)

  • Gentoo Linux (kernel v6.5.10)

Extremely rough measurements:

#threads

1k

10k

100k

500k

1m

5m

run time

1.0s

1.1s

1.3s

3s

6s

20s

Effects

Virtual threads:

  • aren’t "faster threads"

  • remove "number of threads" as bottleneck

  • match app’s unit of concurrency to Java’s

Virtual threads increase throughput:

  • when workload is not CPU-bound

  • when number of concurrent tasks is high

โ‡ simplicity && throughput

Server how-to

For servers:

  • request handling threads are started by web framework

  • frameworks will offer (easy) configuration options

We’re getting there.

Spring Boot

Set property on 3.2 (Nov 2023):

spring.threads.virtual.enabled=true

Quarkus

Annotate request handling method on 3.?:

@GET
@Path("api")
@RunOnVirtualThread
public String handle() {
	// ...
}

Helidon

Just works on 4.0 (currently RC1).

More

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
Unlimited Threads
Under The Hood
String Templates
New & Updated APIs
Performance & More

Preparing your code

Virtual threads:

  • always work correctly

  • may not scale perfectly

Code changes can improve scalability
(and maintainability, debuggability, observability).

Avoid thread pools

Only pool expensive resources
but virtual threads are cheap.

โ‡ Replace thread pools (for concurrency),
with virtual threads plus, e.g., semaphores.

With thread pools

// limits concurrent queries but pools ๐Ÿ‘Ž๐Ÿพ
private static final ExecutorService DB_POOL =
	Executors.newFixedThreadPool(16);

public <T> Future<T> queryDatabase(Callable<T> query) {
	return DB_POOL.submit(query);
}

With semaphore

// limits concurrent queries without pool ๐Ÿ‘๐Ÿพ
private static final Semaphore DB_SEMAPHORE =
	new Semaphore(16);

public <T> T queryDatabase(Callable<T> query)
		throws Exception {
	DB_SEMAPHORE.acquire();
	try {
		return query.call();
	} finally {
		DB_SEMAPHORE.release();
	}
}

Caveats

To understand virtual thread caveats
we need to understand how they work.

(Also, it’s very interesting.)

Under the hood

The Java runtime manages virtual threads:

  • runs them on a pool of carrier threads

  • on blocking call:

    • internally calls non-blocking operation

    • unmounts from carrier thread!

  • when call returns:

    • mounts to (other) carrier thread

    • continues

The simple web request

Remember the hypothetical request:

  1. interpret request

  2. query database (blocks)

  3. process data for response

In a virtual thread:

  • runtime submits task to carrier thread pool

  • when 2. blocks, virtual thread unmounts

  • runtime hands carrier thread back to pool

  • when 2. unblocks, runtime resubmits task

  • virtual thread mounts and continues with 3.

Compatibility

Virtual threads work correctly with everything:

  • all blocking operations

  • synchronized

  • Thread, currentThread, etc.

  • thread interruption

  • thread-locals

  • native code

But not all scale perfectly.

Caveat #1: capture

Despite lots of internal rework (e.g. JEPs 353, 373)
not all blocking operations unmount.

Some capture platform thread:

  • Object::wait

  • file I/O (โ‡ io_uring)

โ‡ Compensated by temporarily growing carrier pool.

โš ๏ธ Problematic when capturing operations dominate.

Caveat #2: pinning

Some operations pin (operations don’t unmount):

  • native method call (JNI)

  • foreign function call (FFM)

  • synchronized block (for now)

โ‡ No compensation

โš ๏ธ Problematic when:

  • pinning is frequent

  • contains blocking operations

Avoid long-running pins

If possible:

  • avoid pinning operations

  • remove blocking operations
    from pinning code sections.

With synchronization

// guarantees sequential access, but pins (for now) ๐Ÿ‘Ž๐Ÿพ
public synchronized String accessResource() {
	return access();
}

With lock

// guarantees sequential access without pinning ๐Ÿ‘๐Ÿพ
private static final ReentrantLock LOCK =
	new ReentrantLock();

public String accessResource() {
	// lock guarantees sequential access
	LOCK.lock();
	try {
		return access();
	} finally {
		LOCK.unlock();
	}
}

Caveat #3: thread-locals

Thread-locals can hinder scalability:

  • can be inherited

  • to keep them thread-local,
    values are copied

  • can occupy lots of memory

(There are also API shortcomings.)

โ‡ Refactor to scoped values (JEP 446).

With thread-local

// copies value for each inheriting thread ๐Ÿ‘Ž๐Ÿพ
static final ThreadLocal<Principal> PRINCIPAL =
	new ThreadLocal<>();

public void serve(Request request, Response response) {
	var level = request.isAdmin() ? ADMIN : GUEST;
	var principal = new Principal(level);
	PRINCIPAL.set(principal);
	Application.handle(request, response);
}

With scoped value

// immutable, so no copies needed ๐Ÿ‘๐Ÿพ
static final ScopedValue<Principal> PRINCIPAL =
	new ScopedValue<>();

public void serve(Request request, Response response) {
	var level = request.isAdmin() ? ADMIN : GUEST;
	var principal = new Principal(level);
	ScopedValue
		.where(PRINCIPAL, principal)
		.run(() -> Application
			.handle(request, response));
}

Preparing your code

Most importantly:

  1. replace thread pools with semaphores

Also helpful:

  1. remove long-running I/O from pinned sections

  2. replace thread-locals with scoped values

  3. replace synchronized with locks

Deprecations

Cleaning house

Already removed:

Cleaning house

Deprecations (for removal):

  • allowed-by-default dynamic agents ใ‰‘ (JEP 451)

  • Windows 32-bit x86 port ใ‰‘ (JEP 449)

  • finalization โ‘ฑ (JEP 421)

  • security manager โ‘ฐ (JEP 411)

  • applet API โ‘ฐ (JEP 398)

  • primitive wrapper constructors โ‘ฏ (JEP 390)

  • biased locking โ‘ฎ (JEP 374)

Agents

What it is:

  • a component that transforms byte code

  • uses java.lang.instrument or JVM TI

  • launches with JVM or attaches later ("dynamic")

Dynamic agents

What you need to know:

  • all mechanisms for agents remain intact

  • nothing changed yet

  • in the future, dynamic attach will be
    disabled by default

  • enable with -XX:+EnableDynamicAgentLoading

Dynamic agents

What you need to do:

  • run your app with -XX:-EnableDynamicAgentLoading

  • observe closely

  • investigate necessity of dynamic agents

Finalization

What it is:

  • finalize() methods

  • a JLS/GC machinery for them

Finalization

What you need to know:

  • you can disable with --finalization=disabled

  • in a future release, disabled will be the default

  • in a later release, finalization will be removed

Finalization

What you need to do:

  • search for finalize() in your code and
    replace with try-with-resources or Cleaner API

  • search for finalize() in your dependencies and
    help remove them

  • run your app with --finalization=disabled and
    closely monitor resource behavior (e.g. file handles)

Security Manager

What it is:

  • a system of checks and permissions

  • intended to safeguard security-relevant
    code sections

  • embodied by SecurityManager

Security Manager

What you need to know:

  • barely used but maintenance-intensive

  • already disallowed by default

  • enable with java.security.manager=allow

  • in a future release, it will be removed

Security Manager

What you need to do:

  • observe your app with default settings
    (โ‡ security manager disallowed)

  • if used, move away from security manager

Primitive constructors

What it is:

  • new Integer(42)

  • new Double(42)

  • etc.

Primitive constructors

What you need to know:

  • Valhalla wants to turn them into value types

  • those have no identity

  • identity-based operations need to be removed

Primitive constructors

What you need to do:

  • Integer.valueOf(42)

  • Double.valueOf(42)

  • etc.

More

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
String Templates
New & Updated APIs
Performance & More

String composition

Composing strings in Java is cumbersome:

String property = "last_name";
String value = "Doe";

// concatenation
String query =
	"SELECT * FROM Person p WHERE p."
		+ property + " = '" + value + "'";
// formatting
String query =
	"SELECT * FROM Person p WHERE p.%s = '%s'"
		.formatted(property, value);

Comes with free SQL injection! ๐Ÿ˜ณ

String interpolation

Why not?

// (fictional syntax!)
String query =
	"SELECT * FROM Person p "
		+ "WHERE p.\{property} = '\{value}'";

Also comes with free SQL injection! ๐Ÿ˜ณ

String interpolation

SQL injections aren’t the only concern.

These also need validation and sanitization:

  • HTML/XML

  • JSON

  • YAML

  • …​

All follow format-specific rules.

String templates

[Preview in Java 21 — JEP 430]

String query = STR."""
	SELECT * FROM Person p
	WHERE p.\{property} = '\{value}'
	""";

Template expression ingredients:

  • template with embedded expressions
    ~> StringTemplate

  • template processor (e.g. STR):
    transforms StringTemplate into String*

Template procesor STR

String form = STR."""
	Desc     Unit   Qty   Amount
	\{desc} $\{price} \{qty} $\{price * qty}

	Subtotal  $\{price * qty}
	Tax       $\{price * qty * tax}
	Total     $\{price * qty * (1.0 + tax)}
	""";
Desc     Unit   Qty   Amount
hammer   $7.88  3     $23.64

Subtotal  $23.64
Tax       $3.546
Total     $27.186

Template processor FMT

String form = FMT."""
	Desc        Unit      Qty   Amount
	%-10s\{desc} $%5.2f\{price} %5d\{qty} $%5.2f\{price * qty}

	Subtotal  $%5.2f\{price * qty}
	Tax       $%5.2f\{price * qty * tax}
	Total     $%5.2f\{price * qty * (1.0 + tax)}
	""";
Desc        Unit      Qty   Amount
hammer      $ 7.88      3   $23.64

Subtotal  $23.64
Tax       $ 3.55
Total     $27.19

Why strings?

Often, strings are just exchange format, e.g.:

  • start with: String + values

  • validate / sanitize (i.e. parse)

  • dumb down to: String ๐Ÿค”

  • parse to: JSONObject, Statement, โ€ฆ

Why the detour?

Custom templating

STR is a singleton instance of
a Processor implementation:

public interface Processor<RESULT, EX> {
	RESULT process(StringTemplate s) throws EX;
}

RESULT can be of any type!

Custom templating

// prevents SQL injections
Statement query = SQL."""
	SELECT * FROM Person p
	WHERE p.\{property} = '\{value}'
	""";

// validates & escapes JSON
JSONObject doc = JSON."""
	{
		"name": "\{name}",
		"year": "\{bday.getYear()}"
	}
	""";

Summary

String templates:

  • simplify string concatenation

  • enable domain-specific processing

  • incentivize the "right way"

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
String Templates
New & Updated APIs
Sequenced Collections
Address Resolution SPI
Misc. Improvements
Performance & More

Order

Collections with order and indexed access:

  • List

Collections with order without indexed access:

  • SortedSet (sort order)

  • Deque (insertion order)

  • LinkedHashSet (insertion order)

  • and more

Sequence

New interfaces capture the concept of order:

  • SequencedCollection

  • SequencedSet

  • SequencedMap

Use as parameter or return type
and enjoy new methods.

Get first

Getting the first element:

  • list.get(0)

  • sortedSet.first()

  • deque.getFirst()

  • linkedHashSet.iterator().next()

Now for all:

sequencedCollection.getFirst()

Remove last

Removing the last element:

  • list.remove(list.size() - 1)

  • sortedSet.remove(sortedSet.last())

  • deque.removeLast()

  • linkedHashSet.๐Ÿคท๐Ÿพโ€โ™‚๏ธ()

Now for all:

sequencedCollection.removeLast()

Reverse order

Reversing order:

  • list.listIterator() โ‡ ListIterator

  • navigableSet.descendingSet() โ‡ NavigableSet (view)

  • deque.descendingIterator() โ‡ Iterator

  • linkedHashSet.๐Ÿคท๐Ÿพโ€โ™‚๏ธ()

Now for all:

sequencedCollection.reversed()

Reversed collection

sequencedCollection.reversed() returns
a SequencedCollection view:

for (E element : list.reversed())
	// ...

sortedSet
	.reversed().stream()
	//...

deque.reversed().toArray();

Reversed view

sequencedCollection.reversed() returns
a SequencedCollection view:

var letters = new ArrayList<>(List.of("A", "B", "C"));
	// โ‡ letters = ["A", "B", "C"]
letters.reversed().removeFirst();
	// โ‡ letters = ["A", "B"]

New operations

void addFirst(E);
void addLast(E);
E getFirst();
E getLast();
E removeFirst();
E removeLast();

SequencedCollection<E> reversed();

(Analoguous for maps.)

New ops vs sort order

What happens when addFirst|Last is used
on a sorted data structure?

SortedSet<String> letters = new TreeSet<>(
	List.of("B", "A", "C"));
	// โ‡ letters = ["A", "B", "C"]
letters.addLast("D");

Options:

  • works always โ‡ breaks SortedSet contract

  • works if value fits โ‡ hard to predict

  • works never โ‡ UnsupportedOperationException

Using types

Use the most general type that:

  • has the API you need/support

  • plays the role you need/support

For collections, that’s often: Collection
(less often: List, Set).

โ‡ Consider new types!

More

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
String Templates
New & Updated APIs
Sequenced Collections
Address Resolution SPI
Misc. Improvements
Performance & More

Built-in resolver

The JDK has a built-in resolver for host names:

  • relies on OS’s native resolver

  • typically uses hosts file and DNS

Desirable improvements:

  • better interaction with virtual threads

  • support for alternative protocols

  • support for testing/mocking

(While being backwards compatible.)

Pluggable resolver

Solution: Allow plugging in a custom resolver.

Two new types:

  • InetAddressResolverProvider

  • InetAddressResolver

Old resolver implements these,
and acts as default.

Pluggable resolver

// registered as a service
public abstract class InetAddressResolverProvider {
	InetAddressResolver get(Configuration config);
	String name();
}

public interface InetAddressResolver {
	Stream<InetAddress> lookupByName(
		String host, LookupPolicy lookupPolicy);
	String lookupByAddress(byte[] addr);
}

A transparent custom resolver

// in module declaration
provides InetAddressResolverProvider
	with TransparentInetAddressResolverProvider;

// class
public class TransparentInetAddressResolverProvider
		extends InetAddressResolverProvider {

	@Override
	public InetAddressResolver get(
			Configuration config) {
		return new TransparentInetAddressResolver(
			config.builtinResolver());
	}

}

A transparent custom resolver

public class TransparentInetAddressResolver
		implements InetAddressResolver {

	private InetAddressResolver builtinResolver;

	public TransparentInetAddressResolver(
			InetAddressResolver builtinResolver) {
		this.builtinResolver = builtinResolver;
	}

	// ...

}

A transparent custom resolver

@Override
public Stream<InetAddress> lookupByName(
		String host, LookupPolicy lookupPolicy) {
	return builtinResolver
		.lookupByName(host, lookupPolicy);
}

@Override
public String lookupByAddress(byte[] addr) {
	return builtinResolver.lookupByAddress(addr);
}

Custom resolvers

Possible resolvers:

  • support for QUIC, TLS, HTTPS, etc.

  • redirect host names to local IPs for testing

  • more ideas?

More

๐Ÿ“ JEP 418

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
String Templates
New & Updated APIs
Sequenced Collections
Address Resolution SPI
Misc. Improvements
Performance & More

Collections

How do you create an ArrayList that
can store 50 elements without resizing?

new ArrayList<>(50);

How do you create a HashMap that
can store 50 pairs without resizing?

new HashMap<>(64, 0.8f);
new HashMap<>(128);

๐Ÿค”

Collections

Right-sizing hashing data structures:

  • HashMap.newHashMap(int numMappings)

  • HashSet.newHashSet(int numElements)

  • LinkedHashMap.newLinkedHashMap(int numMappings)

  • LinkedHashSet.newLinkedHashSet(int numElements)

Math

Lots of new methods on Math:

  • for int/long division with different modes for:

    • rounding

    • overflow handling

  • for ceiling modulus (5 โŒˆ%โŒ‰ 3 = -1)

  • for clamping

Strings and builders

"String".indexOf(
	String str, int beginIndex, int endIndex)

On StringBuilder and StringBuffer:

  • repeat(int codePoint, int count)

  • repeat(CharSequence cs, int count)

๐Ÿ’ฃ๐Ÿ’ฅ

On Character (all static):

  • isEmoji(int codePoint)

  • isEmojiPresentation(int codePoint)

  • isEmojiModifier(int codePoint)

  • isEmojiModifierBase(int codePoint)

  • isEmojiComponent(int codePoint)

Localizing DateTimes

Options for formatting dates/times with DateTimeFormatter:

  • with a fixed pattern: ofPattern

  • with a localized style: ofLocalizedDate
    (FULL, LONG, MEDIUM, SHORT)

What about a localized result with custom elements?

โ‡ DateTimeFormatter.ofLocalizedPattern

Localized patterns

DateTimeFormatter.ofLocalizedPattern:

  • you include what you want to show up
    (e.g. year + month with "yMM")

  • result will depend on locale
    (e.g. "10/2023" in USA)

Pattern comparison:

var now = ZonedDateTime.now();
for (var locale : List.of(
		Locale.of("en", "US"),
		Locale.of("be", "BE"),
		Locale.of("vi", "VN"))) {
	Locale.setDefault(locale);

	var custom = DateTimeFormatter
		.ofPattern("y-MM-dd");
	var local = DateTimeFormatter
		.ofLocalizedDate(FormatStyle.SHORT);
	var customLocal = DateTimeFormatter
		.ofLocalizedPattern("yMM");

	// pretty print
}

Pattern comparison

localecustomlocalboth

en_US

2023-10-02

10/2/23

10/2023

be_BE

2023-10-02

2.10.23

10.2023

vi_VN

2023-10-02

02/10/2023

thรกng 10, 2023

Localized patterns

Analogue methods were added to DateTimeFormatterBuilder:

DateTimeFormatterBuilder appendLocalized(
	String requestedTemplate);

static String getLocalizedDateTimePattern(
	String requestedTemplate,
	Chronology chrono, Locale locale)

More AutoCloseable

These types now implemnet AutoCloseable:

  • HttpClient

  • ExecutorService

  • ForkJoinPool

A better future

Additions to Future<T>:

  • T resultNow()

  • Throwable exceptionNow

  • State state()

New:

enum State {
	RUNNING, SUCCESS, FAILED, CANCELLED
}

More

There are many more additions like this.

Find a few more in
๐ŸŽฅ Java 21 API New Features (Sep 2023)

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
String Templates
New & Updated APIs
Performance & More
Generational ZGC
Class-Data Sharing
Usability
On-Ramp

Generational ZGC

Compared to other GCs, ZGC:

  • optimizes for ultra-low pause times

  • can have higher memory footprint or higher CPU usage

In Java 21, ZGC becomes generational.

Generational Hypothesis

  • most objects die young

  • those who don’t, grow (very) old

GCs can make use of this by tracking
young and old generations.

ZGC didn’t do this, but can do it now with:
-XX:+UseZGC -XX:+ZGenerational

Some Benchmarks

A Cassandra 4 benchmark of ZGC vs GenZGC showed:

  • 4x throughput with a fixed heap or

  • 1/4x heap size with stable throughput

(Probably not representative but very promising.)

More

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
String Templates
New & Updated APIs
Performance & More
Generational ZGC
Class-Data Sharing
Usability
On-Ramp

Class-Data Sharing

Use CDS to shave off 10-25% of your boot times.

Recent improvements:

And more to come from Project Leyden.

More

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
String Templates
New & Updated APIs
Performance & More
Generational ZGC
Class-Data Sharing
Usability
On-Ramp

Usability

Java 21 ๐Ÿ’ฃ๐Ÿ’ฅ

Pattern Matching
Virtual Threads
String Templates
New & Updated APIs
Performance & More
Generational ZGC
Class-Data Sharing
Usability
On-Ramp

Starting (with) Java

Java 21 makes life easier
for new (Java) developers.

Why do we care?

We all know Java, IDEs, build tools, etc.

  • do we all?

  • what about your kids?

  • what about students?

  • what about the frontend dev?

  • what about ML/AI folks?

Java needs to be approachable!
Java needs an on-ramp for new (Java) developers!

Running Java

To write and run a simple Java program, you need:

  • a JDK

  • an editor (IDE?)

  • javac (build tool? IDE?)

  • java (IDE?)

  • some Java code

Writing Java

Minimal Java code:

public class Main {
	public static void main(String[] args) {
		System.out.println("Hello, World!");
	}
}
  • visibility

  • classes & methods

  • static vs instance

  • returns & parameters

  • statements & arguments

Approachability

That’s a lot of tools and concepts!

Java is great for large-scale development:

  • detailed toolchain

  • refined programming model

This make it less approachable.

Let’s change that!

jshell

Java 9 added jshell:

  • all you need:

    • tools: JDK, jshell

    • concepts: statements & arguments

  • but:

    • not great for beginners (IMO)

    • no progression

More is needed.

Single-file execution

Java 11 added single-file execution (JEP 330):

java Prog.java
  • removed: javac

  • but: no progression

Much better for beginners,
but just a section of an on-ramp.

On-Ramp

Expand single-file execution in two directions:

  • simplify code: reduce required Java concepts

  • ease progression: run multiple files with java

Simpler code

Remove requirement of:

  • String[] args parameter

  • main being static

  • main being public

  • the class itself

// all the code in Prog.java
void main() {
	System.out.println("Hello, World!");
}

[Preview in Java 21 — JEP 445]

Running multiple files

Say you have a folder:

MyFirstJava
 โ”œโ”€ Prog.java
 โ”œโ”€ Helper.java
 โ””โ”€ Lib
     โ””โ”€ library.jar

Run with:

java -cp 'Lib/*' Prog.java

[Preview in Java 22 — JEP 458]

Progression

Natural progression:

  • start with main()

  • need arguments? โ‡ add String[] args

  • need to organize code? โ‡ add methods

  • need shared state? โ‡ add fields

  • need more functionality? โ‡ explore JDK APIs

  • even more? โ‡ explore simple libraries

  • need more structure? โ‡ split into multiple files

  • even more โ‡ use visibility & packages

Doesn’t even have to be that order!

Summary

Java’s strengths for large-scale development
make it less approachable:

  • detailed toolchain

  • refined programming model

There are new features that:

  • make it easier to start

  • allow gradual progression

  • entice the future dev generation

More

So long…​

37% off with
code fccparlog

bit.ly/the-jms

More

Slides at slides.nipafx.dev
โ‡œ Get my book!

Follow Nicolai

nipafx.dev
/nipafx

Follow Java

inside.java // dev.java
/java    //    /openjdk

Image Credits