- 1. Getting Started
- 2. Why Upgrade?
- 3. JEPs in Java 26
- 4. Core
- 5. HotSpot
- 6. Language specification
- 7. Security
- 8. Migrating
- 9. Other changes
- 9.1. JDK-8329829 - HttpClient: Add a BodyPublishers.ofFileChannel method
- 9.2. JDK-8334015 - Add support for UUID Version 7 defined in RFC 9562
- 9.3. JDK-8346944 - Unicode 17 support
- 9.4. JDK-8342705 - API documentation dark theme
- 9.5. JDK-8348278 - Reduced default initial heap size
- 9.6. JDK-8354469 - Enhanced keytool password handling when output is redirected
- 9.7. JDK-8354548 - Update CLDR to Version 48.0
- 9.8. JDK-8357653 - Inner classes of type parameters emitted as raw types in signatures
- 9.9. JDK-8362637 - Convert java.nio.ByteOrder to an enum
- 9.10. JDK-8364361 - java.lang.Process should implement Closeable
- 9.11. JDK-8366575 - Remove SDP support
- 9.12. JDK-8369238 - Allow virtual thread preemption on some common class initialization paths
- 9.13. JDK-8369432 - Add Support for JDBC 4.5 MR
- 10. Closing thoughts
- 11. Resources
Java 26, the next STS release that’s part of the six-month release cadence, will be released on the 17th of March 2026, and once again, it helps move the language forward. The previews that are part of this release help shed some light on what we can expect from Java 27 in September 2026.
|
Note
|
Teaser of enhancements
Java 26 gathers ten JEPs across different areas that will enhance performance, security, and modern protocol support, all while steadily progressing major, long-term language projects. With the addition of HTTP/3, significant optimisations for the G1 garbage collector, and a renewed push for program integrity by restricting final field mutation, this release builds directly on the solid foundation of the JDK 25 LTS release. |
To try out Java 26:
-
Install it by:
-
Download from jdk.java.net/26 or use a package manager:
-
Using a package manager:
-
SDKman:
sdk install java 26-open -
Homebrew (macOS):
brew install openjdk@26 -
Chocolatey (Windows):
choco install openjdk26
-
-
-
For preview features, compile and run with
--enable-preview:
javac --enable-preview --release 26 YourFile.java
java --enable-preview YourFile-
Free performance: benefit from improved average application throughput with a leaner G1 garbage collector and faster startup times through ahead-of-time (AOT) object caching that now works with any collector, including the low-latency ZGC.
-
Try out the latest features: continue working with preview and incubator APIs that are maturing towards becoming permanent fixtures, including Structured Concurrency, pattern matching for primitives, and the high-performance Vector API. Please also remember to provide feedback on the relevant mailing lists.
-
Enhanced security and integrity: prepare for a future where
finaltruly meansfinal, enhancing program safety and potential performance. The release also simplifies cryptographic operations with an improved PEM encoding API. -
Modern protocol support: stay current with web standards by seamlessly interacting with HTTP/3 servers using the familiar HTTP Client API with minimal code changes.
-
Reduced technical debt: the final removal of the long-deprecated and obsolete Applet API helps clean up the platform, as it is no longer supported by modern browsers or recent JDK releases
| JEP | Title | Status |
|---|---|---|
Prepare to Make Final Mean Final |
Final |
|
Remove the Applet API |
Final |
|
Ahead-of-Time Object Caching with Any GC |
Final |
|
HTTP/3 for the HTTP Client API |
Final |
|
G1 GC: Improve throughput by reducing synchronization |
Final |
|
PEM Encodings of Cryptographic Objects |
Second Preview |
|
Structured Concurrency |
Sixth Preview |
|
Lazy Constants |
Second Preview |
|
Vector API |
Eleventh Incubation |
|
Primitive Types in Patterns, instanceof, and switch |
Fourth Preview |
This release brings HTTP/3, which helps keep Java up-to-date with the latest specifications, and we see nice progress on ongoing JEPs such as the Vector API.
When deep reflection is used to mutate final fields, warnings will be issued. The intent is to help developers prepare for future releases, where this is restricted to help make our programs safer and maybe faster by facilitating further JVM optimisations. (see for reference: integrity by default).
|
Warning
|
Please pay attention to this JEP if your framework/library heavily depends on reflection, such as serialisation libraries. |
An example of such a warning:
WARNING: Final field foo in x.Y has been [mutated/unreflected for mutation] by class dev.simonverhoeven.z.caller in module Foo (file:/path/to/somefile.jar)
WARNING: Use --enable-final-field-mutation=N to avoid a warning
WARNING: Mutating final fields will be blocked in a future release unless final field mutation is enabledAPI developers can avoid the current warnings and future restrictions by selectively enabling final field mutation where it’s essential.
To enable final field mutation by any code on the class path, regardless of where the field is declared, we can use the command-line option: --enable-final-field-mutation=ALL-UNNAMED.
In case we want to be a bit more fine-grained, we can restrict it to specific modules by using: --enable-final-field-mutation=module1, module2
Do keep in mind that the to-be-mutated field must also be open to the code that’s performing the deep-reflection.
Besides passing in the final-field option directly to the launcher as an option, there are other ways:
-
using an argument-file
-
setting
JDK_JAVA_OPTIONS -
adding it to the manifest of an executable JAR
-
through the
JNI Invocation API, passing the option to an embedded JVM that it creates -
passing it to
jlinkwhen creating a custom Java runtime
We can also control what action the Java runtime takes when an illegal mutation is attempted:
-
--illegal-final-field-mutation=allowwill allow it without warnings -
--illegal-final-field-mutation=warnwhich is the default in JDK26 will allow it with a warning -
--illegal-final-field-mutation=debugwill behave the same as warn, but will also issue a stack trace -
--illegal-final-field-mutation=denywill result inField::setthrowing anIllegalAccessException. This will become the default in a future release
To help facilitate finding code that mutates final fields, it’s recommended to use --illegal-final-field-mutation=debug and/or use the Java Flight Recorder and look for jdk.FinalFieldMutation events.
$ java -XX:StartFlightRecording:filename=catchmutation.jfr ...
$ jfr print --events jdk.FinalFieldMutation catchmutation.jfrThe Applet API was deprecated in JDK 17 through JEP-398.
Generally speaking, this will have a negligible impact given that web browsers no longer support applets.
The following will be removed:
-
java.appletpackage -
java.beans.AppletInitializer -
javax.swing.JApplet
Additionally, references to the above will be removed from:
-
java.beans.Beans -
javax.swing.RepaintManager
The HTTP/3 protocol will now be supported by the HTTP Client API, allowing us to easily interact with HTTP/3 servers with minimal changes. The QUIC protocol will not receive an API through this JEP, nor a server-side implementation.
|
Note
|
HTTP/3
HTTP/3 uses QUIC over UDP, which is a different transport layer, and non-upgradable from HTTP1.1 or HTTP2 connections, which is the reason we need to explicitly select it. |
HTTP/3 brings several improvements over its predecessors:
-
Reduced connection overhead: eliminates head-of-line blocking at the transport layer
-
Faster connection establishment: combines transport and cryptographic handshakes
-
Better handling of packet loss: independent stream handling means loss in one stream doesn’t block others
-
Improved performance on lossy networks: particularly beneficial for mobile connections
-
Better connection migration: maintains connections when switching networks (e.g., WiFi to cellular)
This can be done for all requests by configuring it on the HttpClient object:
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();Or on a request basis:
HttpRequest request = HttpRequest.newBuilder(URI.create("https://github.com/SimonVerhoeven/java26-demo"))
.version(HttpClient.Version.HTTP_3)
.GET().build();It’s important to note that HTTP/3 requires explicit server support. Servers must advertise their HTTP/3 capabilities, typically through the Alt-Svc (Alternative Service) HTTP header or DNS records. When you specify HTTP_3 as the version:
-
The client will attempt to connect using HTTP/3 if the server advertises support
-
If HTTP/3 is unavailable or the connection fails, the client will automatically fall back to HTTP/2 or HTTP/1.1
-
This fallback mechanism ensures your application remains functional even when HTTP/3 isn’t available
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_3)
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/api"))
.build();
// Will use HTTP/3 if available, otherwise falls back to HTTP/2 or HTTP/1.1
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// You can check which protocol version was actually used
System.out.println("Protocol used: " + response.version());|
Note
|
Testing HTTP/3 Support
Before deploying HTTP/3 in production, verify that your target servers actually support it. You can check HTTP/3 availability using online tools or by examining the server’s |
Consider enabling HTTP/3 when:
-
Your application communicates with modern CDNs or cloud services that support HTTP/3 (e.g., Cloudflare, Google Cloud, AWS CloudFront)
-
Your users experience high-latency or lossy network conditions (mobile applications)
-
You need improved performance for API-heavy applications with many concurrent requests
-
You want to future-proof your networking stack
However, keep in mind that:
-
Corporate networks or firewalls may block UDP traffic, preventing HTTP/3 connections
-
Some proxy servers don’t yet support HTTP/3
-
The automatic fallback ensures compatibility, but you should test in your specific environment
-
No server-side HTTP/3 implementation is provided by this JEP
-
No direct API access to the underlying QUIC protocol
-
UDP port 443 must be accessible (some networks block UDP traffic)
-
Initial connection may require an additional round trip to discover HTTP/3 support via
Alt-Svc
This proposal aims to address some of the frequently encountered challenges in concurrent programming by introducing an API that treats groups of related tasks running in different threads as a single unit of work.
The proposed approach, also known as structured concurrency, will help us streamline error handling, cancellation, and observability, making concurrent code more reliable and easier to manage.
The main target is promoting a style of concurrent programming that eliminates common risks such as thread leaks and cancellation delays, while also improving the observability of concurrent code. The API itself is centered around StructuredTaskScope, which allows us to define a clear hierarchy of tasks and subtasks, ensuring that all subtasks are completed or canceled before the parent task exits. This enforces a structured flow of execution, similar to single-threaded code, where subtasks are confined to the lexical scope of their parent task. The API also provides built-in shutdown policies (e.g., ShutdownOnFailure and ShutdownOnSuccess) to handle common concurrency patterns, such as cancelling all subtasks if one fails or succeeds. We can also define our own shutdown policies.
By reifying the task-subtask relationship at runtime, structured concurrency makes it easier for us to reason about and debug concurrent programs. It also integrates well with observability tools, such as thread dumps, which can now display the hierarchical structure of tasks and subtasks.
This enhancement does not aim to replace existing concurrency constructs like ExecutorService or Future, but rather to complement them by offering a more structured and reliable alternative for managing concurrent tasks.
Compared to the fifth preview (JEP-505), which saw several API changes such as the public constructors of StructuredTaskScope being replaced with static factory methods, the changes are relatively small this time:
-
The
allSuccessfulOrThrow()method onJoinernow returns a list of results rather than a stream of subtasks -
The
anySuccessfulResultOrThrow()method onJoinerhas been renamed toanySuccessfulOrThrow -
The static
openmethod ofStructuredTaskScopethat acceptedJoinerandFunctionnow expectsJoinerandUnaryOperator -
A new method
onTimeOuthas been added to theJoinerinterface, which allows implementations to return a result when the timeout expires
In this example, we’re starting three subtasks in parallel with the StructuredTaskScope API and waiting until we get the results of all the subtasks to compose the response. If one of the subtasks were to fail, the other, still-running subtasks will be cancelled, and scope.throwIfFailed() will throw the exception that happened in the failed subtask.
try (final var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Supplier<Person> personTask =
scope.fork(() -> findPerson(userId));
Supplier<Weather> weatherTask =
scope.fork(this::fetchWeather);
Supplier<Activity> activityTask =
scope.fork(() -> findActivity(userId));
scope.join();
scope.throwIfFailed();
final var person = personTask.get();
final var weather = weatherTask.get();
final var activity = activityTask.get();
return new GarderobeSelectionInput(person, weather, activity);
}This preview API, previously known as JEP-502: Stable values, introduces immutable value holders that are at most initialised once, as it will help us move towards deferred immutability through LazyConstant and StableSupplier.
One of the key benefits of stable values is in how they’re handled by the JVM. Internally, the stable values use the internal @Stable annotation, which, despite the values being non-final, marks them as not changing after the initial updates. Thanks to this, these values benefit from the same constant-folding optimisations as final-fields.
Compared to the previous iteration, we see quite a number of changes, so please try out this API and provide feedback on the mailing lists.
-
The API has been renamed from
StableValuetoLazyConstantto better capture the intended high-level use case of a single constant value that is initialized lazily. -
The focus of the API has moved to more high-level use cases, so low-level methods such as
orElseSetandtrySethave been removed. Meanwhile, the factory methods that take value-computing functions remain. -
The factory methods for lazy lists and maps have been moved to their respective interfaces to make them easier to discover
-
The API has been simplified by removing the
functionandintFunctionfactory methods, given that they provided minimal benefits over lazy lists and maps. -
Null is no longer allowed as a computed value to improve performance, and this better aligns lazy constants with other constructs in the language, such as unmodifiable collections and
ScopedValue
For example, let’s define a LazyConstant that initialises a MapData object by loading it only when it’s first accessed:
private final LazyConstant<MapData> mapData =
LazyConstant.of(this::loadMapData); // <= not initialized yet
public Locale getSatelliteLayer() {
// The first call initializes via loadMapData(); subsequent calls reuse the cached value
return mapData.get().getSatelliteLayer();
}The eleventh iteration of an API for vector computations, this time without any significant changes compared to the previous iteration (JEP-508). It aims to be a way to express these in a manner that compiles reliably to optimal vector instructions on supported CPU architectures to achieve better performance than equivalent scalar computations.
Two important changes are the increased usage of auto-vectorisation on supporting x64 (AVX2/AVX-512) and AArch64 (NEON/SVE) architectures, and now leveraging the foreign function and memory API to link native mathematical-function libraries.
The architects have confirmed that the API will remain in incubation until project Valhalla enters preview itself so that the API can leverage the expected performance and in-memory representation enhancements. Hence, why it’s at its 11th incubation.
|
Note
|
Project Valhalla
Valhalla is not part of JDK 26, but the early-access builds are now available for experimentation. |
This release once again brings us JEPs, which provide free performance upgrades just by upgrading!
The ahead-of-time (AOT) cache introduced by Project Leyden will be enhanced so that it can be used with any garbage collector (GC). This notably includes the low-latency Z Garbage Collector (ZGC), as previously GC-specific formats were used that were not compatible with ZGC’s concurrent collection approach.
To achieve this, a neutral, GC-agnostic format will be used to load cached Java objects sequentially into memory, rather than the current approach of directly mapping them into memory in a GC-specific format. This will enable us to combine ZGC’s low latency with AOT’s fast startup, eliminating the historical tradeoff between startup performance and tail latency.
This change will result in faster startup and warmup times.
The goal is to increase the average application throughput when the default G1 garbage collector is being used.
This will be achieved by reducing the synchronization overhead between threads and reducing the size of the injected code for write barriers while making sure that the overall architecture of G1 is maintained.
The language is once again evolving to offer us more flexibility and convenience.
This JEP, first introduced as JEP-455, aims to enhance pattern matching by allowing primitives in all pattern contexts and allowing one to use them with instanceof and switch as well.
if (someObject instanceof int someInt) {
System.out.println("The int was: " + someInt);
}Compared to the third preview in JDK 25 through JEP-507, there are 2 changes.
-
The definition of unconditional exactness has been enhanced: The concept of "unconditional exactness" determines if a type pattern in a
switchwill always match, making subsequent cases unreachable. JEP 530 refines this definition to be more accurate, which in turn improves the next point -
Tighter dominance checks have been introduced in switch constructs: Dominance refers to one pattern matching all the values that another pattern matches. With the updated rules, the compiler can now more reliably detect when a
caselabel is dominated by a previous one (and is therefore unreachable) and will report a compile-time error
This means that a small number of switch constructs that were legal under JEP-507 will be rejected under JEP-530. For instance, a case with a constant label that can be unconditionally converted to the type of a preceding pattern (like a case 42 after a case short s) will now be seen as dominated and cause a compilation error, preventing a potentially unreachable branch of code.
// This was legal in JEP-507 but will fail in JEP-530
switch (someValue) {
case short s -> System.out.println("Short: " + s);
case 42 -> System.out.println("Forty-two"); // Now a compile error!
}In this release, we see ongoing work to improve cryptographic tooling with a simplified API for PEM encoding, making it easier to work with keys, certificates, and other cryptographic objects.
The second preview of this API which introduces a preview API in Java 26 for encoding/decoding cryptographic objects (keys, certificates, CRLs) to/from the PEM (Privacy-Enhanced Mail) format, simplifying a previously manual and error-prone process.
The API centres on DEREncodable, PEMEncoder, and PEMDecoder classes, supporting standards like PKCS#8 and X.509, with built-in encryption for private keys.
The goals include ease of use and interoperability with tools like OpenSSL, thus addressing a gap highlighted by developer surveys.
The design avoids extending legacy APIs such as KeyFactory in favour of a dedicated, immutable, and thread-safe solution, though encrypted keys require password handling via withEncryption()/withDecryption().
It is possible that future iterations may expand support for non-standard PEM types via the PEM class.
As this is a preview feature, you’ll need to enable it through --enable-preview to experiment with it.
Compared to the previous preview in Java 25 through JEP-470, we see some changes:
-
The
PEMRecordclass has been renamed toPEMand now offers adecode()method to return the decoded Base64 content -
The
EncryptedPrivateKeyInfoclass has been expanded with agetKeyPairmethod that allows us to decrypt aPKCS#8-encodedtext containing aPublicKey -
The
PEMEncoderandPEMDecoderclasses now allow us to encrypt and decrypt bothKeyPairandPKCS8EncodedKeySpecobjects.
It’s always recommended to move away from deprecated classes and methods as soon as possible, ideally before they’re actually removed.
In this release, we’ll need to pay attention in case we’re still using Applets.
This API has been removed as part of JEP-504, so if you rely on this, you will need to migrate to a different API. For example, the AWT API contains several alternatives for the Applet class.
If you’re casting the result of a java.net.URLConnection getContent() method to java.applet.AudioClip, the code will need to be adapted to the new return type determined by the JDK’s built-in content handlers.
The Thread.stop() method has been removed; it was initially deprecated in Java 1.2, which was released in December 1998.
If your code still uses this method, you’ll need to refactor to use proper
thread interruption mechanisms.
Instead of:
thread.stop();Use:
thread.interrupt();Beyond the features delivered through enhancement proposals, there are also some other changes.
|
Note
|
This is just a small subset which piqued my interest. |
We can now stream files through FileChannel to a HttpRequest, which means we no longer need to load a file fully in memory beforehand. This is quite an improvement, especially when dealing with large files.
try (FileChannel fileChannel =
FileChannel.open(Path.of("layer.tga"), StandardOpenOption.READ);
HttpClient client = HttpClient.newHttpClient()) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://www.simonverhoeven.dev/upload"))
.POST(BodyPublishers.ofFileChannel(fileChannel, 0, fileChannel.size()))
.build();
HttpResponse<Void> response = client.send(request, HttpResponse.BodyHandlers.discarding());
}Java 26 introduces native support for UUID version 7 (UUIDv7), a time-ordered UUID format standardised in RFC 9562. This new variant embeds a Unix Epoch timestamp (with millisecond precision) in its most significant bits, whilst maintaining randomness in the remaining bits to ensure uniqueness. This makes it particularly well-suited for database applications where insertion order and efficient indexing are important considerations.
The new UUID.ofEpochMillis(long) method allows us to create a UUIDv7 from a timestamp:
// Create a UUIDv7 from the current timestamp
UUID uuid = UUID.ofEpochMillis(System.currentTimeMillis());
System.out.println(uuid); // e.g., 018e9e35-97af-7290-8000-123456789abc
// Each call generates a unique UUID, even with the same timestamp
UUID another = UUID.ofEpochMillis(System.currentTimeMillis());
System.out.println(uuid.equals(another)); // false - randomness ensures uniqueness
// Retrieve the embedded timestamp
long timestamp = uuid.getEpochMillis();
Instant creationTime = Instant.ofEpochMilli(timestamp);
System.out.println("UUID created at: " + creationTime);
// Verify the UUID version
System.out.println("Version: " + uuid.version()); // 7UUIDv7’s time-ordered nature enables natural chronological sorting:
UUID earlier = UUID.ofEpochMillis(Instant.parse("2026-03-17T09:00:00Z").toEpochMilli());
UUID later = UUID.ofEpochMillis(Instant.parse("2026-03-17T10:00:00Z").toEpochMilli());
System.out.println(earlier.compareTo(later) < 0); // true - naturally time-ordered
// This makes UUIDv7 ideal for:
// - Range queries (WHERE id BETWEEN ? AND ?)
// - Pagination with cursor-based approaches
// - Time-based partitioningUUIDv7 complements the existing v4 format rather than replacing it. The choice between them depends on your requirements:
-
Use v4 if you need simplicity, maximum randomness, and broad compatibility are required, and insertion order or time ordering is unimportant.
-
Use v7 if you need insertion order, lexicographical sorting, more efficient database indexing, or want to embed creation time in the identifier.
|
Note
|
Backward Compatibility
UUIDv7 is fully compatible with the existing UUID infrastructure. All standard UUID methods ( |
|
Note
|
Database Performance Benefits
UUIDv7’s time-ordered nature can significantly reduce database index fragmentation compared to random UUIDs, leading to better insertion performance in high-throughput scenarios. In databases with B-tree indices (such as PostgreSQL, MySQL InnoDB, and Oracle Database), time-ordered UUIDs substantially reduce index page splits compared to random UUIDs in sequential insertion scenarios, leading to both improved write throughput and reduced storage overhead. Actual performance gains depend on workload patterns, database configuration, and hardware characteristics. This makes UUIDv7 particularly valuable for:
|
Java 26 adds support for Unicode 17.0, which includes new characters, scripts, and emoji. This ensures Java stays current with the latest text encoding standards.
This release includes 4,803 new characters, of which 7 are new emoji code points, 4 are new scripts, and technical fixes.
Java 26 introduces a dark theme for JavaDoc, making it easier on the eyes for developers who prefer dark mode. This theme can be toggled in the documentation’s header.
The default initial heap size has been reduced when neither Xms nor -XX:InitialRAMPercentage has been used. Prior to this release, the heap was set to 1/64th of the physical RAM by default; now it’s set to 1/500th. To put this into context, on a 32 GiB this resulted in a 512 MiB heap, whereas in Java 26 it will be 64 MiB.
Not only does this represent a significant reduction in the memory consumption of our applications, but it can also speed up the startup of our applications given that the initial data structure the Garbage Collector has to create will be smaller.
This command reads passwords from the system console to avoid them being displayed on the screen.
However, in previous releases, this did not work when the input or output stream was redirected.
As of this release, the password will no longer be displayed on the screen even if the standard output stream is redirected. This improvement has also been made to the jarsigner command and the JAAS TextCallbackHandler API.
This updates the Unicode Common Locale Data Repository (CLDR) in the JDK to CLDR version 48.
This entails:
-
CLDR-16439 zone alias South_Pole was moved to McMurdo, so remove it from Auckland (#4592)
-
CLDR-16665 key/type core values (#4577)
-
CLDR-13688 Correct the first day of the week for Iceland from Monday to Sunday (#4379)
-
CLDR-18464 Add a Before Hijrah era to the Islamic calendars (#4581)
-
CLDR-18572 Add more European Englishes (#4645)
-
CLDR-18253 Add en_JP (#4634)
-
CLDR-18603 Add GyM, GyMEd availableFormats to root, en (#4679)
-
CLDR-18538 root,en: change H patterns without m[s] to add literal 'h' (#4708)
-
CLDR-9814 Recover Turkey metazone names (#4741)
-
CLDR-18788 Fix Russian metazones (#4833)
-
CLDR-13542 Fix Swiss grouping separator (#5019)
This resolves an issue where javac false believes that the inner class of type parameters returns just Object. Now the correct type is being emitted.
class Demonstration {
static abstract class Getters<T> {
abstract class Getter {
abstract T get();
}
}
static class GettersUsage<T, G extends Getters<T>> {
public T test(G.Getter getter) {
return getter.get();
}
}
}This class, which predates the addition of enum to Java, has been converted to an enum:
public enum ByteOrder {
LITTLE_ENDIAN,
BIG_ENDIAN;
private static final ByteOrder NATIVE_ORDER = Unsafe.getUnsafe().isBigEndian() ? BIG_ENDIAN : LITTLE_ENDIAN;
public static ByteOrder nativeOrder() {
return NATIVE_ORDER;
}
}When a process launched via ProcessBuilder is torn down, the caller bears responsibility for closing its streams and ensuring the process terminates properly. Introducing a Process.close() method would provide a straightforward and intuitive means of ensuring all streams are closed, and the process is terminated.
The try-with-resources statement is commonly used to open streams whilst guaranteeing they’re closed upon exiting the block. By implementing AutoCloseable.close(), the completeness of stream closure and process termination can be handled elegantly through try-with-resources.
The close() method would perform the following actions: close each associated stream and destroy the process should it not have already terminated.
The support for Sockets Direct Protocol, a protocol that has been obsolete for over a decade and cannot be used on modern systems, has been removed from the JDK.
This update changes how the JVM manages these waiting states. By leveraging the same mechanisms used for ObjectMonitor (the internal logic behind synchronised blocks), the JVM can now preempt or unmount virtual threads that are waiting for class initialisation. Thus, further reducing when thread pinning might occur.
This means:
-
Improved Robustness: Virtual threads waiting on invokestatic, new, or static field access will no longer hog carrier threads.
-
Deadlock mitigation: The specific "pathological" deadlock described above is largely eliminated because the carrier thread is freed to do other work, such as finishing the very initialisation the virtual thread is waiting for.
-
Seamless scalability: You no longer need to worry as much about "heavy" class initialisers (static blocks that take a long time or perform complex logic), causing a ripple effect that stalls your entire virtual thread pool.
This update to the specification includes the following changes:
-
Deprecate
SQLPermissionfor removal -
Enhance the
Blob/Clob/Array/SQLXML/NClobinterfaces to extend/support AutoClosable -
Add the SQL types
DECFLOAT,JSONtoTypes.JavaandJDBCType.java -
Add the quoted identifier methods that were added to the
Statementinterface in JDK 9 to theConnectioninterface with the exact same wording and methods -
Clarify the
Timestamp::hashCodemethod, which incorrectly indicates that nanos are not used when calculating the hash
Whilst this release encompasses ten JEPs, it’s worth noting that it includes JEP-517: HTTP/3 for the HTTP Client API, which represents one of the most substantial pull requests in recent years. Additionally, the first project Valhalla early-access build has been made available for experimentation.
Beyond the headline features, there are numerous compelling reasons to upgrade. Performance improvements such as the reduced initial heap size and enhanced virtual thread preemption, alongside Unicode 17 support, type 7 UUID creation, and ongoing security enhancements, all contribute to a more robust platform.
This release also resolves a notable issue where inner classes of type parameters were being emitted as raw types (JDK-8357653).
Furthermore, two new Java Flight Recorder events have been introduced to provide deeper insights into application behaviour:
-
StringDeduplication- monitors string deduplication activity -
FinalFieldMutation- tracks final field mutation attempts
These enhancements, whilst perhaps less prominent than the major JEPs, collectively provide tangible benefits simply through upgrading to Java 26.
Some useful resources to dive deeper into the Java ecosystem and stay up to date are:
-
GitHub repository - this article’s code
-
The release notes - The official source for all changes, including new features, bug fixes, and deprecations
-
OpenJDK Quality Group - A group of developers that helps ensure the quality of OpenJDK projects and FOSS in general through initiatives such as promoting the testing of projects on Early Access (EA) builds
-
The Java version almanack - A great resource with details on distributions, and API differences between various releases
-
Foojay - A magnificent Java community offering articles, tutorials, and discussions on the latest in the Java ecosystem
-
SDKman! - a great tool to manage the installation of various tools and languages
-
Inside Java - News updates by Java team members at Oracle
-
Java Community Process - the place where people can propose, discuss, and approve new features through a Java Specification Request (JSR)
© 2026 Simon Verhoeven. Licensed under the MIT License. |