This article is a personal experience and interpretation of Inside Java Newscast #1. Video address: Click here

⎯ ⎯ ⎯ ⎯ ⎯ ⎯ Chapters ⎯ ⎯ ⎯ ⎯ ⎯ ⎯

  • 0:00 – Intro
  • 0:57 – Java 16 — Intro
  • 1:16 – Java 16 — Records
  • 1:43 – Java 16 — Type Pattern Matching
  • 1:58 – Java 16 — Sealed Classes – Preview
  • 2:25 – Java 16 — Stream API
  • 2:51 – Java 16 — HTTP/2 API
  • 3:14 – Java 16 — Unix Domain Sockets
  • 3:32 – Java 16 – Project Panama (Incubating)
  • 4:07 – Java 16 — JDK Flight Recorder
  • 4:39 – Java 16 — JPackage
  • 5:02 – Java 16 — Performance
  • 5:23 – Java 16 — Security
  • 5:48 – Java 16 — Ports
  • 5:55 – Java 16 — Deprecations/Limitations
  • 6:49 – Java 16 — Outro
  • 7:08 – Java 17
  • 7:22 – Java 17 — Another Port
  • 7:34 – Java 17 — Applet for Removal
  • 7:55 – Java 17 — Sealed Classes
  • 8:12 – Outro

Java 16 – Records

Related JEP address:

  • JEP 359: Records(Preview)
  • JEP 384: Records (Second Preview)
  • JEP 395: Records

Records is a feature I’ve looked into implementing in detail: refer to another article I’ve written about Java Record – the default method used and the underlying implementation of related bytecode generation based on precompilation

In simple terms, it is actually (after compilation to view the bytecode can be seen), after compilation, according to the Record source code insert related fields and methods of bytecode, including:

  1. Automatically generated private final fields
  2. Automatically generated full attribute constructor
  3. Automatically generated public getter methods
  4. Automatically generated hashCode(), equals(), toString() methods:
  5. As you can see from the bytecode, the underlying implementation of these three methods is invokeDynamic, another method
  6. Call isObjectMethods.javaIn this classbootstrapmethods

This also made me laugh, I thought this was Project Valhala’s Inline Object already implemented (see my series: Why does this Record have wait()? Synchronized synchronization can also be performed (because the Inline Object of Project Valhala does not have the header of the Inline Object of a normal class and cannot be synchronized with the methods of a normal class Object). Finally, Big Goetz saw at a glance that I was mistaken:

The java-14-feature-spotlight feature was originally designed to accommodate what scenarios and why some designs were discarded. The main points are summarized as follows:

1. The most common use of Java Records is when multiple methods return results. In the past, we might need to wrap objects like apache-Commons pairs or tuples, or create a new type of our own. You can now use Record.

2. The second common application is to keep the object as it passes through a Stream and reduce operations, such as Stream sorting:

List<Player> topN
        = players.stream()
             .sorted(Comparator.comparingInt(p -> getScore(p)))
             .limit(N)
             .collect(toList());
Copy the code

In this way, getScore(p) is called once for every comparison, which is O(n^2). With Record you can reduce computation with less code and changes:

record PlayerScore(Player player, Score score) {
    // convenience constructor for use by Stream::map
    PlayerScore(Player player) { this(player, getScore(player)); }
}

List<Player> topN
    = players.stream()
             .map(PlayerScore::new)
             .sorted(Comparator.comparingInt(PlayerScore::score))
             .limit(N)
             .map(PlayerScore::player)
             .collect(toList());
Copy the code

Finally, I recommend this article about the serialization of Record some analysis and thinking: Java Record some thinking – serialization related

Java 16 — Type Pattern Matching

Related JEP address:

  • JEP 305: Pattern Matching for instanceof (Preview)
  • JEP 375: Pattern Matching for instanceof (Second Preview)
  • JEP 394: Pattern Matching for instanceof

Type-pattern matching has long been a popular feature, but it works best when combined with Sealed Class and Patterns in Switch in the next section, which we’ll cover in more detail.

Description of Type Pattern Matching

Nicolai’s post on Type Pattern Matching is very detailed and summarizes as follows:

The original code would look like this:

void feed(Animal animal) { if (animal instanceof Elephant) { ((Elephant) animal).eatPlants(); } else if (animal instanceof Tiger) { ((Tiger) animal).eatMeat(); }}Copy the code

Now you can write it like this:

void feed(Animal animal) {
	if (animal instanceof Elephant elephant)
		elephant.eatPlants();
	else if (animal instanceof Tiger tiger)
		tiger.eatMeat();
}
Copy the code

No null pointer is needed, because instanceof already has its own null judgment, and eligible Type Pattern Matching variables are not null. Also, Type Pattern Matching does not support upmatching because it makes no sense, i.e. the following code compiles an error:

public void upcast(String string) {
	// compile error
	if (string instanceof CharSequence sequence)
		System.out.println("Duh");
}
Copy the code

Another common place to implement equals is:

// old @Override public boolean equals(Object o) { if (this == o) return true; if (! (o instanceof Equals)) return false; Type other = (Type) o; return someField.equals(other.someField) && anotherField.equals(other.anotherField); } // new @Override public final boolean equals(Object o) { return o instanceof Type other && someField.equals(other.someField) && anotherField.equals(other.anotherField); }Copy the code

A Type Pattern Matching is a grammar candy

In fact, this feature is a syntax sugar, we can simply test:

public class TypePatternMatching { public static void main(String[] args) { Object object = new Object(); if (object instanceof String s) { System.out.println("a"); }}}Copy the code

To view the compiled bytecode:

public class test.TypePatternMatching { public test.TypePatternMatching(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class java/lang/Object 3: dup 4: invokespecial #1 // Method java/lang/Object."<init>":()V 7: astore_1 8: aload_1 9: instanceof #7 // class java/lang/String 12: ifeq 28 15: aload_1 16: checkcast #7 // class java/lang/String 19: astore_2 20: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream; 23: ldc #15 // String a 25: invokevirtual #17 // Method java/io/PrintStream.println:(Ljava/lang/String;) V 28: return }Copy the code

As you can see, bytecode is written the same way:

public static void main(String[] args) { Object object = new Object(); if (object instanceof String) { String s = (String)object; System.out.println("a"); }}Copy the code

If you decompile this class, you can see that.

Java 16 – Sealed Classes – Preview

Sealed Class (JEP) Sealed Class (JEP)

  • JEP 360: Sealed Classes (Preview)
  • JEP 397: Sealed Classes (Second Preview)
  • JEP 409: Sealed Classes

In some cases, we might want to enumerate all implementation classes of an interface, for example:

interface Shape { }
record Circle(double radius) implements Shape { }
record Rectangle(double width, double height) implements Shape { }

double area(Shape shape) {
	if (shape instanceof Circle circle)
		return circle.radius() * circle.radius() * Math.PI;
	if (shape instanceof Rectangle rect)
		return rect.width() * rect.height();
	throw new IllegalArgumentException("Unknown shape");
}
Copy the code

How can we be sure that we have enumerated all of our shapes? Sealed Class allows us to solve this problem by specifying which classes can be inherited by a Sealed Class:

sealed interface Shape permits Rectangle, Circle {} record Circle(double radius) implements Shape {} record Rectangle(double width, double height) implements Shape {} double area(Shape shape) { if (shape instanceof Circle circle) return circle.radius()  * circle.radius() * Math.PI; if (shape instanceof Rectangle rect) return rect.width() * rect.height(); throw new IllegalArgumentException("Unknown shape"); }Copy the code

Sealed Class (may be an abstract Class or interface) specifies the names of all the implementation classes. For inherited classes, there are the following restrictions:

  • Sealed Class must be in the same module as Sealed Class. If no module is specified, they must be in the same package
  • Each inherited Class must inherit Sealed Class directly, not indirectly
  • Each inherited class must be one of three:
    • Final class, Java Record itself is final
    • Sealed’s class, which further specifies which subclasses will be implemented
    • A non-sealed class is also an extension to sealed class, but sealed class does not know or care what other subclasses of this class may be.

Here’s an example:

sealed interface Shape permits Rectangle, Circle, Triangle, WeirdShape {}
	
record Circle(double radius) implements Shape {}
record Rectangle(double width, double height) implements Shape {}


sealed interface Triangle extends Shape permits RightTriangle, NormalTriangle {}
record RightTriangle(double width, double height) implements Triangle {}
record NormalTriangle(double width, double height) implements Triangle {}

static non-sealed class WeirdShape implements Shape {}
class Star extends WeirdShape {}

double area(Shape shape) {
	if (shape instanceof Circle circle)
		return circle.radius() * circle.radius() * Math.PI;
	if (shape instanceof Rectangle rect)
		return rect.width() * rect.height();
	if (shape instanceof RightTriangle rt)
		return rt.width() * rt.height() / 2;
	if (shape instanceof NormalTriangle nt)
		return nt.width() * nt.height() / 2;
	throw new IllegalArgumentException("Unknown shape");

}
Copy the code

Pattern Matching for Switch is a convenient feature, but currently in Java 17, Pattern Matching for Switch is Preview: JEP 406: Pattern Matching for switch (Preview). We need to add –enable-preview to the compile and launch parameters so that we can write code like this:

double area(Shape shape) {
	return switch (shape) {
		case Circle circle -> circle.radius() * circle.radius() * Math.PI;
		case Rectangle rect -> rect.width() * rect.height();
		case RightTriangle rt -> rt.width() * rt.height() / 2;
		case NormalTriangle nt -> nt.width( ) * nt.height() / 2;
		default -> throw new IllegalArgumentException("Unknown shape");
	};

}
Copy the code

Java 16 — Stream API update

There are two updates to the Stream API in Java 16. As an aside, if you want to see the differences between JDK versions and which apis have been added or removed, you can check out this link:

  • Javaalmanac. IO/JDK / 17 / apid…

The two versions in the path are the two versions to be compared, and their interfaces are as follows:

We can also use the JDK built-in JDEPS tool to find expired and deprecated apis and their replacements


jdeps --jdk-internals -R --class-path 'libs/*' $project
Copy the code

Libs is the directory where all your dependencies are located, and $project is your project jar package.

. JDK Internal API Suggested Replacement ---------------- --------------------- sun.misc.BASE64Encoder Use Java.util.base64 @since 1.8 Sun.reflect.Reflection Use Java.lang.StackWalker @since 9Copy the code

I’ve written an article about this update: Some thoughts on the Stream interface in Java 16. The core content is summarized as follows:

Suppose you have the mail Record class, which contains the ID and the mailbox to which it was sent and the mailbox to which it was cc:

record Mail(int id, Set<String> sendTo, Set<String> cc) {}
Copy the code

We want to find all the different contacts for a batch of emails and put them in a List, maybe something like this:

Set<String> collect = mails.stream().flatMap(mail -> {
	Set<String> result = new HashSet<>();
	result.addAll(mail.sendTo());
	result.addAll(mail.cc());
	return result.stream();
}).collect(Collectors.toSet());
Copy the code

However, it is not elegant to create an extra Set and corresponding Stream for each Mail, and pass through sendTo and cc twice for each Mail (addAll once, Stream again). In fact, we are currently just taking cc and sendTo out of mail and participating in the subsequent Stream. In this scenario, mapMulti is a good fit:

Set<String> collect = mails.stream().<String>mapMulti((mail, consumer) -> {
	mail.cc().forEach(consumer::accept);
	mail.sendTo().forEach(consumer::accept);
}).collect(Collectors.toSet());
Copy the code

As can be seen:

  • The input parameter to mapMulti is oneBiConsumerIn fact, it isUse the consumer in its argument to receive objects that follow the Stream
  • The idea behind mapMulti is to continue the Stream by passing the object in the argument that needs to participate in the subsequent Stream to the consumer
  • consumer There are no restrictions on object typesTo restrict, you must take a parameter<String>Otherwise, the final return isSet<Object>Rather thanSet<String>

For Stream, toList is directly converted into List. Since truncation operation in COLLECT is not involved, it occupies less memory and requires fewer and faster operations than COLLECT. Before converting it to a List, you need to collect(Collectors. ToList ()). The generated List is an ArrayList and can be mutable. However, the new Api toList generates is UnmodifiableList, which is immutable. So these two apis cannot be replaced directly with each other, and you need to do some checking to make sure there are no changes.

Java 16 – HTTP/2 API

Java 16 also introduced two JDK additions to the HTTP/2 API, see:

  • JDK-8252304: Seed an HttpRequest.Builder from an existing HttpRequest
  • JDK-8252382: Add a new factory method to concatenate a sequence of BodyPublisher instances into a single publisher

Java 16 — Unix Domain Sockets

Relevant JEP:

  • JEP 380: Unix-Domain Socket Channels

Unix Domain Sockets are named as local files, allowing us to access local network connections as if they were local files. This is used for communication between different processes deployed on the same machine. Here is a simple BIO example:

// create UnixDomainSocketAddress Path socketFile = path. of("/home/zhanghaxi/process1"); UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketFile); / / the server monitoring ServerSocketChannel serverChannel = ServerSocketChannel. Open (StandardProtocolFamily. UNIX); serverChannel.bind(address); SocketChannel channel = serverChannel.accept(); / / the client connected SocketChannel channel = SocketChannel. Open (StandardProtocolFamily. UNIX); channel.connect(address);Copy the code

Examples of the NIO, please refer to: docs.oracle.com/en/java/jav…

Because Unix Domain Sockets know they’re accessing a local process, there are fewer checks and validations (such as addressing and routing) than there are TCP/IP local loopback connections, and packets are smaller because they don’t have to do these checks. Unix Domain Sockets are supported on Linux, MacOS, Windows 10 and beyond, and Windows Server 2019 and beyond.

Java 16 – Project Panama (Incubating)

Project Panama, an effort to make Java more comprehensive, is still being incubated. It currently includes the following three apis:

  • Vector API: Enables Java to also use new CPU instructions such as SIMD (Single Instruction Multiple Data) related instructions to optimize computation speed
  • Foreign Linker API: Allows Java to call system libraries directly, without having to wrap another layer through JNI.
  • Foreign-memory Access API: Allows Java to operate directly on external Memory, breaking through the limitations of existing external Memory apis, and is also an API that consolidates existing out-of-heap Memory operations.

Vector API

Relevant JEP:

  • JEP 338: Vector API (Incubator)
  • JEP 414: Vector API (Second Incubator) : In Java 17
  • JEP 417: Vector API (Third Incubator) : In Java 18

One of the main applications is the use of THE CPU’s SIMD (Single instruction multiple data) processing, which provides a multi-channel data flow through the program, which may have four channels or eight channels, or any number of channels through which a single data element flows. And the CPU organizes operations on all channels in parallel at once, which can greatly increase CPU throughput. With the Vector API, the Java team is working to give Java programmers direct access to it using Java code; In the past, they had to program vector mathematics at the assembly code level or use C/C++ with the intrinsics, which were then made available to Java via JNI.

One of the main optimization points is loops, old loops (scalar loops) that execute on one element at a time, and that’s slow. You can now use the Vector API to convert scalar algorithms into faster data parallel algorithms. An example of using Vector:

// The Throughput index is @benchmarkmode (mode.throughput) // The Throughput index is @benchmarkmode (mode.throughput) // The Throughput index is @benchmarkmode (mode.throughput) @warmup (iterations = 1) // You can go to @fork (1) // And iterations = 10. We test 10 times. @Measurement(Iterations = 10) @state (value = scope.benchmark) Public class VectorTest {private static final VectorSpecies<Float> SPECIES  = FloatVector.SPECIES_256; final int size = 1000; final float[] a = new float[size]; final float[] b = new float[size]; final float[] c = new float[size]; public VectorTest() { for (int i = 0; i < size; I++) {a [I] = ThreadLocalRandom. Current () nextFloat (0.0001 f to 100.0 f); [I] = b ThreadLocalRandom. Current () nextFloat (0.0001 f to 100.0 f); } } @Benchmark public void testScalar(Blackhole blackhole) throws Exception { for (int i = 0; i < a.length; I++) {c [I] = (a * a [I] [I] [I] [I] * b + b) * 1.0 f; } } @Benchmark public void testVector(Blackhole blackhole) { int i = 0; Int upperBound = species.loopbound (a.length); int upperBound = species.loopbound (a.length); // Each loop processes the number of SPECIES.length() for (; i < upperBound; i += SPECIES.length()) { // FloatVector va, vb, vc; var va = FloatVector.fromArray(SPECIES, a, i); var vb = FloatVector.fromArray(SPECIES, b, i); var vc = va.mul(va) .add(vb.mul(vb)) .neg(); vc.intoArray(c, i); } for (; i < a.length; I++) {c [I] = (a * a [I] [I] [I] [I] * b + b) * 1.0 f; } } public static void main(String[] args) throws RunnerException { Options opt = new OptionsBuilder().include(VectorTest.class.getSimpleName()).build(); new Runner(opt).run(); }}Copy the code

Note that using the incubating Java feature requires additional startup arguments to expose the module, in this case –add-modules JDK.incubator. Vector. These arguments need to be added to both the javac compilation and the Java runtime, using IDEA:

Test results:

Benchmark Mode Cnt Score Error Units VectorTest. TestScalar THRPT 10 7380697.998 ± 1018277.914 OPS /s Vectortestvector THRPT 10 37151609.182 ± 1011336.900 ops/sCopy the code

Fizzbuzz-simd-style is an interesting article (although the performance optimization seems to be not only due to SIMD, but also due to algorithm optimization).

Foreign Linker API

Relevant JEP:

  • JEP 389: Foreign Linker API (Incubator)
  • JEP 412: Foreign Function & Memory API (Incubator) : Integrated with the Foreign Linker API in Java 17
  • JEP 419: Foreign Function & Memory API (Second Incubator) : Located in Java 18

Using this API, we can call the system’s libraries using pure Java code, for example, using Java code to pop up a Windows prompt:

The above example from headcrashing.wordpress.com/2021/02/06/… If you are interested, you can check it out

Foreign-Memory Access API

  • JEP 370: Foreign-Memory Access API (Incubator)
  • JEP 383: Foreign-Memory Access API (Second Incubator)
  • JEP 393: Foreign-Memory Access API (Third Incubator)
  • JEP 412: Foreign Function & Memory API (Incubator) : Integrated with the Foreign Linker API in Java 17
  • JEP 419: Foreign Function & Memory API (Second Incubator) : Located in Java 18

Many popular high-performance Java frameworks and middleware use out-of-heap memory, but there are currently inadequate apis in Java to manipulate out-of-heap memory:

  • The ByteBuffer API can provide direct memory access (DirectBuffer and MMAP Buffer), but the size is limited (2 GB) because Buffer classes are limited by 32-bit Addressing. And there are many problems that have been left unsolved for years, such as: mappedByteBuffer.release ()/close() to… , Please make DirectByteBuffer performance enhancements, Add Absolute bulk put and get methods
  • Unsafe APIS: High performance, JIT-optimized, but not restricted to access to any memory. Accessing an already freed memory will crash the JVM.
  • JNI calls: poor performance because they cannot be jIT-optimized (e.g. method inlining)

If these apis are developed, manipulating memory using Java will be much easier and more efficient

Java 16 — JDK Flight Recorder

JFR is one of my favorite Java features, and I’ve written many articles about it. I’ve used JFR to locate performance bottlenecks and online problems. Please refer to the following series or articles:

  • JFR full solution series
  • Location and resolution of avalanche problems caused by JFR
  • JFR fixes CPU Load overload due to SSL
  • Locate the Log4j2 log output blocking problem
  • Spring-data-redis millions of QPS are under too much pressure to connect and fail, I’m fucking stupid

In Java 16, JFR streams exposed through JMX were added to the JFR Streams introduced in Java 14 for JFR. JFR Events: JDK-8253898: JFR: Remote Recording Stream

16 – jpackage Java

Relevant JEP:

  • Deprecated JEP 311: Java Packager API & CLI
  • JEP 343: Packaging Tool (Incubator)
  • JEP 392: Packaging Tool

This is a tool for packaging Java programs into installable packages. Currently supported operating systems and formats include:

  • Linux: deb and rpm
  • macOS: pkg and dmg
  • Windows: msi and exe

Check out this article: Building self-contained, Installable Java Applications with JEP 343: Packaging Tool

Java 16 – Performance

There are many performance-related updates

Hotspot implements Elastic Metaspace

Related JEP: Elastic Metaspace

The original dimension implementation, each class loader occupy a single dimension abstraction, as the class loader is recycled, the memory is released but will not return to the system but continue to other class loaders reuse, dimension of the system memory footprint will only increase will not be closed, also is not return memory to the system. This has now been optimized to dynamically scale the metacarspace. This piece of detailed source analysis, I will issue a similar to the net after the most core TLAB analysis of the article analysis of this piece.

G1 and Parallel GC optimization

If you want to learn more about this optimization, check out JDK 16 G1/Parallel GC Changes

ZGC optimization

Relevant JEP:

  • JEP 376: ZGC: Concurrent Thread-Stack Processing

The ZGC has basically made every phase of GC concurrent, so the GC root scan still needs STW. The JEP optimizes the thread stack scan in the GC root scan so that the scan can also be “semi-parallelized”. I will analyze this in detail in the future.

Shenandoah GC optimization

  • Shenandoah: should not block pacing reporters
  • Shenandoah: reconsider free budget slice for marking
  • Shenandoah: pacer should wait on lock instead of exponential backoff
  • Shenandoah: support manageable SoftMaxHeapSize option
  • Shenandoah: Concurrent weak reference processing
  • Shenandoah: “adaptive” heuristic is prone to missing load spikes

Java 16 – Security

For security-related optimizations, see JDK 16 Security Enhancements

Java 16 – Deprecations/Limitations

Primitive Wrapper Warnings

Relevant JEP:

  • JEP 390: Warnings for Value-Based Classes

This is an exciting update, paving the way for my long-awaited Project Valhala (yes, the one I got wrong about Record earlier).

Currently, the constructor flags for primitive wrapper type classes (such as Integer) are expired and will be removed in future versions, replaced by static methods inside them, valueOf().

I wrote a separate article to analyze this, referring to the introduction to Project Valhalla in JEP Interpretation and Early Tasting Series 4 – Java 16

Strong Encapsulation By Default

Relevant JEP:

  • JEP 396: Strongly Encapsulate JDK Internals by Default
  • JEP 403: Strongly Encapsulate JDK Internals: Java 17 release, completely removed--illegal-accessIf used, there will beOpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=warn; support was removed in 17.0The prompt.

In order to promote Java modularity, the — illegal-Access feature has been modified. Before Java 16, the default is permit. If you encounter a package that is not open, you will receive a prompt for the first time, but it still works normally:

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by j9ms.internal.Nimbus
	(file:...) to constructor NimbusLookAndFeel()
WARNING: Please consider reporting this
	to the maintainers of j9ms.internal.Nimbus
WARNING: Use --illegal-access=warn to enable warnings
	of further illegal reflective access operations
WARNING: All illegal access operations will be denied
	in a future release
Copy the code

Java 16 is deny. That is, illegal packet access is prohibited by default. You can change this parameter by using the startup parameter –illegal-access=permit. Java 17 removes this parameter and invalidates the startup parameter, prompting an error that reflects access to internally unexposed packages, such as:

var dc = ClassLoader.class.getDeclaredMethod("defineClass",
		String.class,
		byte[].class,
		int.class,
		int.class);
dc.setAccessible(true);
Copy the code

Run with the start parameter –illegal-access= WARN:

OpenJDK 64-Bit Server VM warning: Ignoring option --illegal-access=warn; The support was removed in 17.0 the Exception in the thread "is the main" Java. Lang. Reflect. InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int) throws  java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @378bf509 at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199) at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)Copy the code

However, packet control can still be broken with the startup parameter — Add-attach java.base/java.lang= all-unnamed.