
With Java 11, the first Long-Term Support (LTS) release of the JDK since the switch to the six-month release cycle was published on September 25, 2018. “Long-Term Support” means that the JDK vendors provide this version with security patches for years.
Oracle’s Premier Support for Java 11 ended on September 30, 2023; the paid Extended Support runs until January 31, 2032. Eclipse Temurin provides free updates until October 2027.
The last LTS release was Java 8. Java 9 and 10 were not LTS releases, which means that support for these versions was discontinued with each subsequent release.
I have sorted the changes in Java 11 according to relevance for daily developer work. First come changes to the language itself. Followed by enhancements to the JDK class library, tools, and experimental features. And finally, deprecations, deletions, and other minor changes.
It is also important to know that Oracle JDK 11 – like all versions up to Java 16 – can only be used freely by developers. For production use, companies need a paid subscription from Oracle. OpenJDK 11, on the other hand, is free to use for everyone.
Since Java 17, the Oracle JDK has been available under a free license (NFTC) – but only for the updates released until one year after the next LTS release.
Local-Variable Syntax for Lambda Parameters
JDK Enhancement Proposal 323 allows the use of “var” in parameters of implicitly typed lambda expressions.
What is an implicitly typed lambda expression?
Let’s start with an explicitly typed lambda expression. In the following example, explicit means that the data types of the lambda parameters l and s – i.e., List<String> and String – are specified:
(List<String> l, String s) -> l.add(s);
However, the compiler can also derive types from the context so that the following – implicitly typed – notation is also permitted:
(l, s) -> l.add(s);
Since Java 11, you can use the “var” keyword introduced in Java 10 instead of the explicit types:
(var l, var s) -> l.add(s);
But why write “var” when you can omit the types completely, as in the example before?
The reason for this is annotations. If a variable is to be annotated, the annotation must be placed at the type – an explicit type or “var”. An annotation is not permitted to be placed on the variable name.
If you want to annotate the variables in the above example, only the following notation was possible before Java 11:
(@Nonnull List<String> l, @Nullable String s) -> l.add(s);
Java 11 now also allows the following, shorter variant with “var”:
(@Nonnull var l, @Nullable var s) -> l.add(s);
The different notations must not be mixed. That means you must either specify the type for all variables, omit all types, or use “var” for all variables.
Which form you ultimately choose depends on the readability in the specific case and the style guidelines of your team.
HTTP Client (Standard)
Before Java 11, using native JDK resources to, for example, send data via HTTP POST required a lot of code.
public String post(String url, String data) throws IOException {
URL urlObj = new URL(url);
HttpURLConnection con = (HttpURLConnection) urlObj.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");
// Send data
con.setDoOutput(true);
try (OutputStream os = con.getOutputStream()) {
byte[] input = data.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
// Handle HTTP errors
if (con.getResponseCode() != 200) {
con.disconnect();
throw new IOException("HTTP response status: " + con.getResponseCode());
}
// Read response
String body;
try (InputStreamReader isr = new InputStreamReader(con.getInputStream());
BufferedReader br = new BufferedReader(isr)) {
body = br.lines().collect(Collectors.joining("\n"));
}
con.disconnect();
return body;
}
JDK 11 includes the new HttpClient class, which significantly simplifies working with HTTP.
You can write the code above much shorter and more expressive using HttpClient:
public String post(String url, String data) throws IOException, InterruptedException {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request =
HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofString(data))
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new IOException("HTTP response status: " + response.statusCode());
}
return response.body();
}
The new HttpClient class is highly versatile:
- In the example above, we send a String via
BodyPublishers.ofString(). Using the methodsofByteArray(),ofByteArrays(),ofFile(), andofInputStream()of the same class, we can read the data to be sent from various other sources. - Similarly, the
BodyHandlersclass, whoseofString()method we use to get the response as a String, can also return byte arrays and streams or store the received response in a file. HttpClientalso supports HTTP/2 and WebSocket, unlikeHttpURLConnection.- Furthermore, in addition to the synchronous programming model shown above,
HttpClientprovides an asynchronous model. TheHttpClient.sendAsync()method returns aCompletableFuture, which we can then use to continue working asynchronously.
For example, an asynchronous variant of the post() method might look like this:
public void postAsync(
String url, String data, Consumer<String> consumer, IntConsumer errorHandler) {
HttpClient client = HttpClient.newHttpClient();
HttpRequest request =
HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/json")
.POST(BodyPublishers.ofString(data))
.build();
client
.sendAsync(request, BodyHandlers.ofString())
.thenAccept(
response -> {
if (response.statusCode() == 200) {
consumer.accept(response.body());
} else {
errorHandler.accept(response.statusCode());
}
});
}
(HttpClient is defined by JDK Enhancement Proposal 321.)
New Collection.toArray() Method
Before Java 11, the Collection interface provided two toArray() methods to convert collections to arrays. The following example shows these two methods (and two different usages of the second method) using a String list as an example:
List<String> list = List.of("foo", "bar", "baz");
Object[] strings1 = list.toArray();
String[] strings2a = list.toArray(new String[list.size()]);
String[] strings2b = list.toArray(new String[0]);
The first toArray() method (without parameters) returns an Object array, because due to Type Erasure, the type information of list is no longer known at runtime.
The second toArray() method expects an array of the requested type. If this array is at least as large as the collection, the elements are stored in this array (strings2a). Otherwise, a new array of the needed size is created (strings2b).
Since Java 11, we can also write the following:
String[] strings = list.toArray(String[]::new);
This method allows the Collection classes to create an array of the necessary size using the passed array constructor reference.
However, this possibility is not used. The default method in the Collection interface creates an empty array and passes it to the toArray() method with an array parameter:
default <T> T[] toArray(IntFunction<T[]> generator) {
return toArray(generator.apply(0));
}
In Java 11, only the wrappers from Collections, such as unmodifiableCollection() and synchronizedCollection(), override this default method – and they pass the call on to the wrapped collection. ArrayList, HashSet, and all other collection classes from java.util and java.util.concurrent do not override it.
There is no JEP for this change; it is tracked in the bug tracker under JDK-8060192.
New String Methods
In Java 11, the String class has been extended with some helpful methods:
String.strip(), stripLeading(), stripTrailing()
The String.strip() method removes all leading and trailing whitespace from a String.
Isn’t that what we already have the String.trim() method for?
Yes, with the following difference:
trim()removes all characters with a code point U+0020 or smaller. This includes, for example, “space”, “tab”, “newline”, and “carriage return”.strip()removes those characters thatCharacter.isWhitespace()classifies as whitespace. On the one hand, these are some (but not all) characters with code point U+0020 or smaller. And on the other hand, characters defined in the Unicode Standard as spaces, line breaks, and paragraph separators (e.g., U+2002 – a space as wide as the letter “n”).
There are two variants of the method: stripLeading() removes only leading whitespace, stripTrailing() only trailing whitespace.
String.isBlank()
The new String.isBlank() method returns true if and only if the String is empty or contains only those characters that the Character.isWhitespace() method mentioned in the previous point classifies as whitespace.
String.repeat()
You can use String.repeat() to repeatedly concatenate a String:
System.out.println(":-) ".repeat(10));
:-) :-) :-) :-) :-) :-) :-) :-) :-) :-)
String.lines()
The String.lines() method splits a String at line breaks and returns a Stream of all lines.
Here’s a quick example:
Stream<String> lines = "foo\nbar\nbaz".lines();
lines.forEachOrdered(System.out::println);
foo
bar
baz
There is no JEP for these extensions of the String class; they are tracked in the bug tracker under JDK-8200377 (strip(), stripLeading(), stripTrailing()), JDK-8200436 (isBlank()), JDK-8197594 (repeat()), and JDK-8200380 (lines()).
Files.readString() and writeString()
Reading and writing text files has been continuously simplified since Java 6. In Java 6, we had to open a FileInputStream, wrap it with an InputStreamReader and a BufferedReader, then load the text file line by line into a StringBuilder (alternatively, omit the BufferedReader and read the data in char[] blocks) and close the readers and the InputStream in the finally block.
Luckily, we had libraries like Apache Commons IO that did this work for us with the FileUtils.readFileToString() and writeStringToFile() methods.
In Java 7, we could create the nested stream/reader or stream/writer combinations much easier using Files.newBufferedWriter() and Files.newBufferedReader(). And thanks to try-with-resources, we didn’t need a finally block anymore.
However, several lines of code were still necessary:
public static void writeStringJava7(Path path, String text) throws IOException {
try (BufferedWriter writer =
Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
writer.write(text);
}
}
private static String readFileJava7(Path path) throws IOException {
StringBuilder sb = new StringBuilder();
try (BufferedReader reader =
Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append('\n');
}
}
return sb.toString();
}
Java 11 finally gives us methods that can rival those of third-party libraries:
Files.writeString(path, text, StandardCharsets.UTF_8);
text = Files.readString(path, StandardCharsets.UTF_8);
You can also omit the charset parameter. Both methods then use UTF-8 – regardless of the operating system’s default charset.
There is no JEP for this change; it is tracked in the bug tracker under JDK-8201276.
Path.of()
Before Java 11, we had to create Path objects via Paths.get() or File.toPath(). Since Java 8, interfaces can contain static methods. This allowed the JDK developers to integrate appropriate factory methods directly into the Path interface.
Since Java 11, you can create path objects as follows, for example:
// Relative path foo/bar/baz
Path.of("foo/bar/baz");
Path.of("foo", "bar/baz");
Path.of("foo", "bar", "baz");
// Absolute path /foo/bar/baz
Path.of("/foo/bar/baz");
Path.of("/foo", "bar", "baz");
Path.of("/", "foo", "bar", "baz");
As parameters, you can specify the whole path or parts of the path – in any combination, as shown in the example.
To define an absolute path, the first part must start with “/” on Linux and macOS – and with a drive letter, such as “C:\” on Windows.
There is no JEP for this change; it is tracked in the bug tracker under JDK-8194746.
Optional.isEmpty() and Predicate.not()
Two small additions make conditions more readable that previously needed a negation.
The Optional.isEmpty() method is the counterpart to isPresent(): it returns true if an Optional contains no value.
The following example checks an empty Optional:
Optional<String> name = Optional.empty();
System.out.println(name.isEmpty());
true
The static method Predicate.not() negates a predicate. This lets you negate a method reference, too – which you could otherwise only invert as a lambda expression with !.
The following example uses Predicate.not(String::isBlank) to filter all strings from a stream that are empty or consist only of whitespace:
List<String> words = Stream.of("foo", " ", "bar", "")
.filter(Predicate.not(String::isBlank))
.collect(Collectors.toList());
System.out.println(words);
[foo, bar]
There is no JEP for these two changes; they are tracked in the bug tracker under JDK-8184693 and JDK-8050818.
Epsilon: A No-Op Garbage Collector
With JDK 11, we get a new garbage collector: Epsilon GC.
Epsilon GC does … nothing. Well, not quite. It manages the allocation of objects on the heap – but it has no garbage collection process to release the objects again.
What is the purpose of a garbage collector that does not collect garbage?
The following scenarios are conceivable:
- Performance tests: In microbenchmarks, for example, where you compare different implementations of algorithms with each other, a regular garbage collector is a hindrance, as it can influence the execution times and thus falsify the measurement results. By using Epsilon GC, you can exclude such influences.
- Extremely short-lived applications, such as those developed for AWS Lambda, should be terminated as quickly as possible. A garbage collection cycle would be a waste of time if the application was terminated a few milliseconds later anyway.
- Eliminating latencies: If developers have a good understanding of the memory requirements of their application and entirely (or almost entirely) dispense with object allocations, Epsilon GC enables them to implement a latency-free application.
Epsilon GC is an experimental feature. That is why you need two options on the java command line to activate it:
-XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC
(Epsilon GC is defined by JDK Enhancement Proposal 318.)
Launch Single-File Source-Code Programs
For small Java programs consisting of only one class, JDK Enhancement Proposal 330 becomes interesting.
This makes it possible to compile and execute a .java file using the java command. Furthermore, a .java file can be made executable using a so-called “shebang”.
I’ll show you precisely what that means with a simple example.
Create a file with the name Hello.java and the following content:
public class Hello {
public static void main(String[] args) {
if (args.length > 0) {
System.out.printf("Hello %s!%n", args[0]);
} else {
System.out.println("Hello!");
}
}
}
Before Java 11, you had to compile this program with javac first and then run it with java:
$ javac Hello.java
$ java Hello Anna
Hello Anna!
Starting with Java 11, you can omit the first step:
$ java Hello.java Anna
Hello Anna!
The source code is compiled into the working memory and executed from there.
On Linux and macOS, you can go one step further and directly write an executable Java script. To do this, you have to insert a so-called “shebang” and the source version in the first line:
#!/usr/bin/java --source 11
public class Hello {
public static void main(String[] args) {
if (args.length > 0) {
System.out.printf("Hello %s!%n", args[0]);
} else {
System.out.println("Hello!");
}
}
}
The file must not have a .java extension. Rename it to Hello and make it executable:
mv Hello.java Hello
chmod +x Hello
Now you can execute it directly:
$ ./Hello Anna
Hello Anna!
Pretty cool. For smaller tools, this can be very useful.
Nest-Based Access Control
When using inner classes, we Java developers frequently face the following warning:

What is it all about?
The Java Language Specification (JLS) allows access to private fields and methods of inner classes. The Java Virtual Machine (JVM), on the other hand, did not allow this up to Java 10.
To resolve this contradiction, the Java compiler (up to Java 10) inserts so-called “synthetic accessor methods” when accessing these private fields and methods – with default “package-private” visibility.
These additional methods result in seemingly private fields and methods being accessible from the entire package. Accordingly, the warning occurs.
Up to Java 10, you could solve this problem by either making the corresponding members package-private yourself or – at least in Eclipse – annotating the code with @SuppressWarnings("synthetic-access").
JDK Enhancement Proposal 181 extends the JVM’s access control mechanisms to allow access to private members of inner classes without synthetic accessors.
Should you have made methods and fields of inner classes package-private or used @SuppressWarnings for the above reason, you can undo this after upgrading to Java 11.
Analysis Tools
Java Flight Recorder
Numerous tools help us analyze and fix errors during the development process. However, certain problems only occur at the runtime of an application. Analyzing them is often difficult or impossible, as we are often unable to reproduce such errors.
Java Flight Recorder (JFR) can assist us by recording JVM data at runtime and making it available in a file for subsequent analysis.
Flight Recorder had existed for several years as a commercial feature in the Oracle JDK. With JDK Enhancement Proposal 328, it becomes part of the OpenJDK and can thus be used freely.
How to Start Flight Recorder?
You can start Flight Recorder in two ways. Firstly, you can activate it at the start of an application using the following option on the java command line:
-XX:StartFlightRecording=filename=<file name>
Secondly, you can use jcmd to activate Flight Recorder in a running Java application:
jcmd <pid> JFR.start filename=<file name>
You can specify numerous options; for example, you can use “duration” to specify how long the recorder should run. To present all options in detail would go beyond the scope of this article. You can find them in Oracle’s documentation of the java command (option -XX:StartFlightRecording) and the jcmd command (JFR.start).
Java Flight Recorder Example
In the following example, let 31100 be the process ID of the Java application to be analyzed. You start the recording as follows (we specify a name for the recording via the optional “name” parameter):
$ jcmd 31100 JFR.start filename=myrecording.jfr name=myrecording
31100:
Started recording 1. No limit specified, using maxsize=250MB as default.
Use jcmd 31100 JFR.dump name=myrecording to copy recording data to file.
Flight Recorder writes the specified file only when the recording ends – at the latest when you exit the application. However, you can also save the recording in between by executing the dump command that was displayed at startup:
$ jcmd 31100 JFR.dump name=myrecording
31100:
Dumped recording "myrecording", 344.8 kB written to:
<path>/myrecording.jfr
If you did not specify a “name” parameter at startup, you can specify the recording number (in the example above “1”) as the name.
You can stop Flight Recorder as follows:
$ jcmd 31100 JFR.stop name=myrecording
31100:
Stopped recording "myrecording".
How do you analyze the file saved by Flight Recorder?
JDK Mission Control
To view the collected data, you need another tool: JDK Mission Control. On the project’s GitHub page, you can find links to several distributors where you can download Mission Control for Windows, Mac, and Linux.
Click “File / Open File…” to load the analysis file. Mission Control first shows you an overview of the collected data:

Using the navigation on the left, you can then take a closer look at individual areas such as threads, memory usage, and locks. In “Threads”, for example, you can see which threads ran from when to when:

The other navigation points also contain exciting presentations of the collected data. Try it out for yourself right now!
Low-Overhead Heap Profiling
An important tool for analyzing memory problems (e.g., high garbage collector latencies or OutOfMemoryErrors) are heap dumps for analyzing the objects located on the heap. The market offers numerous tools for this purpose. Before Java 11, these tools did not reveal at which point in the code the objects located on the heap were created.
With JEP 331, the Java Virtual Machine Tool Interface (JVMTI) – i.e., the interface through which these tools obtain the data about the running application – is extended by the possibility to collect samples of object allocations including their stack traces. You define the sampling interval yourself. The heap analysis tools can display this additional information and thus make it much easier for us developers to analyze problems.
Experimental Features
I will only briefly discuss experimental features and instead refer to the Java versions in which they were released as “production-ready”.
ZGC: A Scalable Low-Latency Garbage Collector (Experimental)
The “Z Garbage Collector” – ZGC for short – is an alternative garbage collector developed by Oracle with the goal of limiting GC pauses to a maximum of 10 ms – without reducing the overall throughput by more than 15% compared to G1GC.
In Java 11, ZGC is only available for Linux/x64; macOS and Windows were added in Java 14. You can enable it with the following JVM flags:
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
ZGC became production-ready in Java 15; I describe it in more detail in the article on that version.
(This experimental release is defined by JDK Enhancement Proposal 333.)
Deprecations and Deletions
This section lists all features marked as “deprecated” or removed from the JDK.
Remove the Java EE and CORBA Modules
JDK Enhancement Proposal 320 removes the following modules from the JDK:
- java.xml.ws (JAX-WS)
- java.xml.bind (JAXB)
- java.activation (JAF)
- java.xml.ws.annotation (Common Annotations)
- java.corba (CORBA)
- java.transaction (JTA)
- java.se.ee (aggregator module for the six previously mentioned modules)
- jdk.xml.ws (tools for JAX-WS)
- jdk.xml.bind (tools for JAXB)
The web services stack – JAX-WS, JAXB, JAF, and Common Annotations – was originally developed for the Java EE platform and was added to the standard edition “Java SE” with Java 6. CORBA has been part of Java SE since 1998, parts of JTA since Java 1.3. All these modules were marked “deprecated” in Java 9 and finally removed with Java 11.
Should you miss these libraries after upgrading to Java 11, you can pull them back into your project, e.g., via Maven dependencies.
Deprecate the Nashorn JavaScript Engine
The JavaScript engine “Nashorn” introduced in JDK 8 was marked as “deprecated for removal” with JEP 335 in Java 11 and removed in Java 15 with JEP 372.
The reason for this is the pace at which ECMAScript (the standard behind JavaScript) evolves – it made maintaining Nashorn too costly.
Deprecate the Pack200 Tools and API
Pack200 is a special compression method introduced in Java 5 that achieves higher compression rates than standard methods, especially for .class and .jar files. Pack200 was developed to save as much bandwidth as possible in the early 2000s.
However, the algorithm is complex, and the further development costs are no longer in proportion to the benefits in times of 100-Mbit Internet lines.
Therefore, the tool has been marked as “deprecated” with JDK Enhancement Proposal 336 and removed in Java 14 with JEP 367.
JavaFX Goes Its Own Way
Starting with Java 11, JavaFX (and the associated javapackager tool) is no longer shipped with the Oracle JDK – the OpenJDK builds never included it. Instead, you can download it as a separate SDK from the JavaFX homepage.
The deployment stack for applets and Java Web Start applications was also removed from the Oracle JDK.
There is no JEP for these changes; Oracle lists them in the Java 11 release notes.
Other Changes in Java 11 (Which You Don’t Necessarily Need to Know as a Java Developer)
This section lists other changes under the hood of Java 11 that you probably won’t notice directly. Nevertheless, it may be useful to skim the following sections.
Unicode 10
With JDK Enhancement Proposal 327, Java 11 has been extended to include support for Unicode 10.0. Since Java 10 still supported Unicode 8.0, the changes from Unicode 9.0 are included as well.
What does that mean?
The String and Character classes in particular had to be extended with the new code blocks and characters. This is relevant, for example, for String.toUpperCase() and toLowerCase() as well as for Character.getName() and Character.UnicodeBlock.of().
Here is a short code example (the Unicode code point 0x1F929 stands for the emoji 🤩):
System.out.println("name = " + Character.getName(0x1F929));
System.out.println("block = " + Character.UnicodeBlock.of(0x1F929));
Java 10 knows the Unicode block the emoji belongs to, but not the emoji itself:
name = null
block = SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS
Java 11, on the other hand, also knows the new emoji – what luck ;-)
name = GRINNING FACE WITH STAR EYES
block = SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS
For String.toUpperCase() and toLowerCase(), the Osage and Adlam scripts are relevant: they were added in Unicode 9.0 and have uppercase and lowercase letters.
The following example converts the Osage capital letter A (U+104B0) with toLowerCase() and prints the code point of the result:
String osageA = new String(Character.toChars(0x104B0));
System.out.printf("%X%n", osageA.toLowerCase().codePointAt(0));
Java 10 does not know the letter and leaves it unchanged:
104B0
Java 11 returns the Osage small letter A (U+104D8):
104D8
Improve Aarch64 Intrinsics
JDK Enhancement Proposal 315 improves existing and adds new so-called “intrinsics” for the AArch64 platform (i.e., 64-bit ARM CPUs).
Intrinsics are used to execute architecture-specific assembly code instead of Java code, which significantly improves the performance of specific JDK class library methods.
This JEP adds intrinsics for trigonometric functions and the logarithm function, and optimizes existing intrinsics for methods such as String.compareTo() and String.indexOf().
Transport Layer Security (TLS) 1.3
Before Java 11, the JDK supported the following security protocols:
- Secure Socket Layer (SSL) version 3.0
- Transport Layer Security (TLS) versions 1.0, 1.1, and 1.2
- Datagram Transport Layer Security (DTLS) versions 1.0 and 1.2
JEP 332 extends this list to include the modern security standard TLS 1.3.
TLS 1.0 and 1.1 have been disabled by default since Java 16 (JDK-8202343) – and, via a backport, in Java 11 since update 11.0.11.
ChaCha20 and Poly1305 Cryptographic Algorithms
JEP 329 adds two cryptographic algorithms to the JDK – ChaCha20 and Poly1305. They are used, for example, by the security protocols mentioned in the previous section.
Key Agreement with Curve25519 and Curve448
With JEP 324, the key exchange protocols of the JDK are extended by the elliptic curves “Curve25519” and “Curve448”. Key agreement according to RFC 7748 is more efficient and secure than the previously available ECDH scheme (Elliptic Curve Diffie-Hellman).
Dynamic Class-File Constants
The .class file format was extended to include the CONSTANT_Dynamic constant, offering “language designers and compiler implementors broader options for expressivity and performance”. Should you plan to develop a language or compiler, you can find more details in JDK Enhancement Proposal 309.
Complete List of All Changes in Java 11
This article has presented all the features of Java 11 that are defined in JDK Enhancement Proposals, as well as the most important enhancements to the JDK class library that are not associated with any JEP.
For a complete list of changes, see the official Java 11 Release Notes.
Summary
Java 11 now allows us to use “var” in lambda parameters.
We can conveniently access HTTP interfaces using the new HttpClient.
String has been extended with some helpful methods. Using Files.readString() and writeString(), we can finally read and write text files with a single line of code without a third party library, and with Path.of(), we can create Path objects more concisely than with Paths.get().
We can use Epsilon GC to perform microbenchmarks without disruptive GC cycles.
We can compile and execute small programs consisting of only one class with a single java call – or even make them executable using a “shebang”, as we know it from Perl, for example.
Thanks to Nest-Based Access Control, the compiler no longer needs to insert synthetic access methods.
And with Flight Recorder, a handy analysis tool, which previously could only be used with a support contract from Oracle, has been made freely available to everyone. I can only recommend trying it out!
Was this article helpful to you? Then I’d be happy if you took a moment to leave a review on my ProvenExpert profile.




