- 1. Getting Started
- 2. Why Upgrade?
- 3. JEPs in Java 27
- 4. Core
- 5. HotSpot
- 6. Language specification
- 7. Security
- 8. Migrating
- 9. Other Changes
- 9.1. JDK-8210336 - ISO-8601 short zone offset support
- 9.2. JDK-8296183 -
jndiprovider.propertiescontains properties pointing to non-existing classes - 9.3. JDK-8347112 - Javadoc copies subdirectories of doc-files by default
- 9.4. JDK-8355522 - Remove the
java.locale.useOldISOCodessystem property - 9.5. JDK-8357439 - Add bash autocompletion for jcmd
- 9.6. JDK-8364182 - Add jcmd VM.security_properties command
- 9.7. JDK-8371656 - HashMap.putAll() optimisations
- 9.8. JDK-8380841 - Add AVX2 optimised intrinsic for binarySearch on primitive arrays
- 9.9. JDK-8382727 - Remove the deprecated
-noclassgc,-noverify,-Xverify:none, and-verifyremoteoptions from the java launcher - 9.10. JDK-8382582 - Removal of the JVM Compiler Interface (JVMCI)
- 10. Closing thoughts
- 11. Resources
Java 27, the next STS release that’s part of the six-month release cadence, will be released on the 15 September 2026, and once again, it helps move the language forward.
|
Note
|
Teaser of enhancements
Java 27 gathers nine JEPs across different areas that will enhance performance, memory efficiency, security, and concurrent programming, all while continuing to advance major language features. With the default adoption of the G1 garbage collector across all environments, compact object headers reducing heap size, and post-quantum cryptography support for TLS 1.3, this release improves our application performance and security. Meanwhile, structured concurrency reaches its seventh preview, providing developers with more reliable patterns for managing concurrent tasks, while new capabilities in pattern matching, lazy constants, and cryptographic object encoding continue to mature through their respective preview cycles. |
To try out Java 27:
-
Install it, either by:
-
Downloading it from jdk.java.net/27, or
-
Using a package manager:
-
SDKman:
sdk install java 27-open -
Homebrew (macOS):
brew install openjdk@27 -
Chocolatey (Windows):
choco install openjdk27
-
-
-
For preview features (JEP-531, JEP-532, JEP-533, JEP-538), compile and run with
--enable-preview:
javac --enable-preview --release 27 YourFile.java
java --enable-preview YourFile|
Important
|
Preview features may be withdrawn or changed in future releases
Preview APIs and language features are fully specified and implemented, but not permanent.
Class files compiled with Note also that |
-
The Vector API (JEP-537) is an incubator module rather than a preview feature, so it needs a different flag: the module has to be added explicitly at both compile and run time, and
--enable-previewdoes nothing for it.
javac --add-modules jdk.incubator.vector YourFile.java
java --add-modules jdk.incubator.vector YourFile|
Note
|
Preview vs. incubator
The two mechanisms are easy to confuse:
Using an incubator module also produces a warning at both compile and run time, and incubator modules are not resolved by default even when they are present in the JDK image. |
-
Performance gains with no effort required: The G1 garbage collector is now the default across all platforms, including resource-constrained environments. After years of refinement, (particularly the throughput work in Java 26), G1 performs comparably to the Serial GC on smaller machines whilst delivering superior latency characteristics. Compact Object Headers are enabled by default, which, during the development team’s testing, reduced heap consumption by 10 to 20 per cent and may improve throughput by 5 to 10 per cent in applications handling many short-lived objects. All these benefits, without us having to change a single line of code!
-
Security improvements that work automatically: Java Flight Recorder now automatically hides sensitive data that was captured in environment variables, system properties, and command-line arguments. This detection is based upon pattern-based filtering (for instance, anything matching
password,token, orapi*key). This prevents inadvertent leakage of credentials and access tokens in production recordings. Furthermore, TLS 1.3 connections now employ post-quantum hybrid key exchange algorithms that offer protection against potential future attacks where adversaries record encrypted traffic today for decryption once quantum computers become available. The platform selectsX25519MLKEM768by default when supported by the server, requiring no configuration adjustments. -
Maturing preview features to experiment with: Several APIs continue advancing through their preview cycles whilst progressing towards finalisation. Structured Concurrency (now in its seventh preview) provides proven patterns for managing concurrent work that eliminate common pitfalls such as thread leaks and delayed cancellation. Pattern matching for primitives (fifth preview iteration) enables more natural type checking logic. The Vector API (twelfth incubator release) offers access to CPU-level vector operations for computation-intensive workloads. If we choose to adopt these features, feedback submitted via the OpenJDK mailing lists directly influences their final design.
-
Streamlined cryptographic operations: Handling PEM-encoded keys, certificates, and CRL data has been streamlined through the new API, rather than the more cumbersome approach we previously had to use. The implementation supports the standard encodings such as PKCS#8 and X.509, with straightforward handling of encrypted private keys. This addresses pain points identified in community surveys regarding interoperability with OpenSSL and similar tools.
-
Practical lazy initialisation support: The Lazy Constants API offers a clean way to defer expensive object creation until actually needed, with new Lazy Sets for implementing feature toggles and conditional logic without concurrency headaches. When a lazy value is initialised, the JVM marks it with the
@Stableannotation internally, allowing constant folding optimisations to apply. We avoid the verbosity of double-checked locking or the initialisation-on-demand holder idiom whilst maintaining thread safety. -
Maintaining a healthy platform: Outdated experimental APIs and long-deprecated options have been removed (including JVMCI, and VM options such as
-noverify,-Xverify:none, and-noclassgc). This pruning keeps the codebase maintainable and reduces complexity for the OpenJDK team.
| JEP | Title | Status |
|---|---|---|
Make G1 the Default Garbage Collector in All Environments |
Final |
|
Post-Quantum Hybrid Key Exchange for TLS 1.3 |
Final |
|
Lazy constants |
Third preview |
|
Primitive Types in Patterns, instanceof, and switch |
Fifth preview |
|
Structured Concurrency |
Seventh preview |
|
Compact Object Headers by Default |
Final |
|
JFR In-Process Data Redaction |
Final |
|
Vector API |
Twelfth incubator |
|
PEM Encodings of Cryptographic Objects |
Third preview |
This JEP, previously known as JEP-526: Lazy constants (Second preview) in Java 26, and as JEP-502: Stable Values in Java 25 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 lazy constants is in how they’re handled by the JVM. Internally, the lazy constants 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 two changes:
-
Removal of low-level methods: The
isInitializedandorElsemethods have been removed to ensure the API’s usage remains strictly aligned with its primary design objectives. -
Introduction of
Set.ofLazy(…): This new factory method has been added to facilitate the creation of a stableSetfrom a predefined collection of element candidates. This addition completes the suite of lazy-initialisation options, ensuring thatList,Set, andMapall possess corresponding lazy versions.
For example, let’s defer both a single expensive value and the elements of a collection:
// We defer the initialisation, it'll be initialised at most once and is immutable.
// This enables constant-folding.
private final LazyConstant<Integer> meaningOfLife = LazyConstant.of(() -> 42);
private final List<BigDecimal> numbers = List.ofLazy(42, BigDecimal::valueOf);
Integer getMeaningOfLife() {
// The constant is computed on the first get() invocation.
return meaningOfLife.get();
}
BigDecimal getNumber(int index) {
// Each element is computed on its first access, independently of the others.
return numbers.get(index);
}List.ofLazy(…) takes the size of the list and a mapper that produces the element for a given index, whereas Set.ofLazy(…) takes a collection of candidate elements. Both defer the actual computation until the moment an element is first accessed, and both compute each element at most once.
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 centred around StructuredTaskScope, which allows us to define a clear hierarchy of tasks and subtasks, ensuring that all subtasks are completed or cancelled 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., awaitAllSuccessfulOrThrow and anySuccessfulOrThrow) 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 sixth preview (JEP-525), the following changes have been made.
-
Introduction of the
R_XType Parameter: Both theStructuredTaskScopeandJoinerinterfaces now incorporate a third type parameter,R_X. This identifies the specific exception type that may be thrown by theStructuredTaskScope.join()method. -
New
openMethod inStructuredTaskScope: A staticopenmethod is now available to implement the default join policy. It employs aUnaryOperatorto determine the configuration for theStructuredTaskScope. -
Updated
JoinerFactory Methods: TheallSuccessfulOrThrow(),anySuccessfulOrThrow(), andawaitAllSuccessfulOrThrow()methods have been modified to create joiners that trigger anExecutionExceptionduringjoin()if the outcome is unsuccessful. Furthermore, new overloads have been introduced, allowing the use of aFunctionto define custom exception types. -
Removal of
awaitAll(): TheJoiner.awaitAll()factory method has been officially removed from the API. -
Replacement of
onTimeout(): TheonTimeout()method has been superseded by thetimeout()method. This new method either yields the result or throws an exception upon a timeout-driven cancellation. Should an exception be thrown bytimeout(), it will feature aCancelledByTimeoutExceptionas its cause.
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.join() will throw the exception that happened in the failed subtask.
try (final var scope = StructuredTaskScope.open()) {
Supplier<Person> personTask =
scope.fork(() -> findPerson(userId));
Supplier<Weather> weatherTask =
scope.fork(this::fetchWeather);
Supplier<Activity> activityTask =
scope.fork(() -> findActivity(userId));
scope.join();
final var person = personTask.get();
final var weather = weatherTask.get();
final var activity = activityTask.get();
return new GarderobeSelectionInput(person, weather, activity);
}This code is quite a bit more robust and easier to read than in the "classic" unstructured concurrency style with an ExecutorService.
The StructuredTaskScope API makes it clear that the three subtasks are related and should be treated as a single unit of work. It also provides a clear mechanism for handling failures and cancellations, which can be tricky to manage in unstructured concurrency.
In the "traditional" approach, where we use an ExecutorService we will need to provide quite a bit of boilerplate code. If fetchWeather throws an exception, the remaining tasks keep consuming resources in the background unless we manually catch the exception and cancel each outstanding Future.
With StructuredTaskScope, the moment fetchWeather fails, the scope automatically triggers cancellation for findPerson and findActivity, unwinding cleanly and preventing background resource leaks without requiring any cleanup logic.
The twelfth iteration of an API for vector computations, this time without any significant changes compared to the previous iteration (JEP-529).
The Vector API gives us a portable way to express numerical algorithms in Java that explicitly leverage SIMD (Single Instruction, Multiple Data) CPU capabilities. Rather than relying on the HotSpot JIT compiler’s often hit-or-miss auto-vectorisation, it allows us to write code that predictably compiles into optimal vector instructions, such as x64 AVX or ARM NEON. This benefits compute-intensive tasks like machine learning, image processing, and cryptography, and falls back to standard scalar execution on hardware without native vector support.
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. Valhalla is not part of JDK 27, but early-access builds are available for experimentation.
The Hotspot JVM will now always select the Garbage-First (G1) garbage collector, even on devices with only one CPU or less than 1792 MiB of RAM. If we were previously using the Serial collector, there might be a minor performance hit, but as always, when changing our GC we will need to measure the impact for our specific use case.
In case we do run into issues, the serial GC can be re-enabled through -XX:+UseSerialGC.
On 64-bit architectures, compact object headers will be made the default in the HotSpot JVM. This will shrink object headers from 96 bits down to 64 bits, thus reducing heap size, improving deployment density, and increasing data locality.
The heap consumption will be reduced by roughly 10-20 per cent, and the throughput increased by 5-10 per cent thanks to this change when upgrading to Java 27. The results vary based on how many small objects our applications have.
There have been several experiments to showcase the benefits of enabling compact object headers, and the results are promising. The following are some of the experiments that have been conducted:
-
A highly parallel JSON parser benchmark by Andriy Plokhotnyuk ran in 10% less time with compact object headers enabled.
-
Roman Kennke demonstrated a setting where the SPECjbb2015 benchmark uses 22% less heap space and 8% less CPU time during his FOSDEM 2025 Lilliput presentation.
-
Stefan Johansson demonstrated a setup on the JDK Bug System where the number of performed garbage collections during the SPECjbb2015 benchmark was reduced by 15% with compact object headers enabled with both G1 and Parallel collectors, prepared in advance of his JavaOne 2025 presentation.
In case we would prefer not to use the compact object headers yet, we can use -XX:-UseCompactObjectHeaders. But we do need to keep in mind that this option will be removed in a future release.
Every in-memory Java object has two parts: the object headers, and the actual payload data. Given the former’s changing, let’s look a bit closer at it.
The object header consists of two parts:
-
Mark word consisting of:
-
25 unused bits
-
31-bit identity hashcode
-
1 unused bit, which was used in the past by the Concurrent Mark Sweep GC which was removed in Java 14
-
4-bit age (used by the Garbage Collector to determine when to move an object from the young to the old generation)
-
1 unused bit, which was used by biased locking, which was removed in Java 15
-
lock (used for synchronised locking, for example)
-
-
class word that contains the 32-bit offset to the class metadata in the Compressed Class space, so the Java Virtual Machine knows which class an object belongs to.
| Mark Word (64 bits) | Class Word (32 bits) | |||||
|---|---|---|---|---|---|---|
Unused |
Identity Hash Code |
Unused |
Age |
Unused |
Lock bits |
Compressed Class Pointer |
25 bits |
31 bits |
1 bit |
4 bits |
1 bit |
2 bits |
32 bits |
Here, the Mark and Class words have been merged into a 64-bit value.
Now the 27 unused bits are used as follows:
-
4 reserved bits for Value classes
-
1 self-forwarded tag bit
-
22 bits for a compressed class pointer
| Compact Object Header (64 bits) | |||||
|---|---|---|---|---|---|
Compressed Class Pointer |
Identity Hash Code |
Reserved |
Age |
Self-forwarded tag |
Tag bits |
22 bits |
31 bits |
4 bits |
4 bits |
1 bit |
2 bits |
|
Note
|
The Self-forwarded Tag is a bit set when a garbage collection copy operation fails, allowing a compact object header to mark the object as remaining in place without overwriting its essential class pointer with a self-referential pointer. |
The JDK Flight Recorder will now redact command-line arguments and the initial values of environment variables and system properties in recordings, so as not to leak sensitive information.
If we need to adjust or fine-tune what gets masked, we can configure JFR’s redaction behaviour using sub-options on -XX:FlightRecorderOptions:
-
redact-key: Specifies filters for key-value pairs across environment variables and system properties. -
redact-argument: Specifies filters for command-line arguments.
Filters rely on standard glob patterns, where * and ? serve as wildcards. Whenever a filter matches a key or argument, its value is replaced with [REDACTED] in the recording.
Multiple filters can be separated by semicolons and are evaluated in order, while multiple sub-options within -XX:FlightRecorderOptions are separated by commas.
|
Note
|
Troubleshooting redaction filters
If we need to troubleshoot or verify how our redact-argument and/or redact-key filters are matching, we can launch the JVM with |
So if we wanted to mask nationaldebt, we could do it like this:
java -XX:FlightRecorderOptions:'redact-key=*debt*' \
-XX:StartFlightRecording:filename=dump.jfr \
-Dnationaldebt=42 \
-jar application.jarAnd verify it in this manner:
$ jfr print \
--events InitialSystemProperty,InitialEnvironmentVariable \
dump.jfr
[...]
jdk.InitialSystemProperty {
startTime = 17:39:02.244 (2026-02-15)
key = "nationaldebt"
value = "[REDACTED]"
}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);
}There are no changes between the fourth and fifth iteration JEP-530; some more feedback is just desired.
This enhances the security of our applications by implementing hybrid key exchange algorithms for TLS 1.3. These algorithms help safeguard our applications against future quantum computing attacks by leveraging both traditional and quantum-resistant algorithms. If our application uses the javax.net.ssl APIs, then we will get these benefits by default, without needing to change our code.
To achieve this, the JDK’s TLS 1.3 implementation will be enhanced with support for three new post-quantum hybrid key exchange schemes that combine ML-KEM with the traditional Ephemeral Elliptic-Curve Diffie-Hellman (ECDHE) algorithms:
-
X25519MLKEM768: Hybrid scheme combining ECDHE with X25519 and ML-KEM-768
-
SecP256r1MLKEM768: Hybrid scheme combining ECDHE using the secp256r1 curve with ML-KEM-768
-
SecP384r1MLKEM1024: Hybrid scheme combining ECDHE using the secp384r1 curve with ML-KEM-1024
These schemes are also referred to as named groups, so the names of these have been added to the Named groups section of the Java Security Standard Algorithm Names specification.
The JDK’s TLS 1.3 implementation will place the X25519MLKEM768 hybrid scheme at the front of the supported KEM list, thus making it the most preferred. This means we won’t have to make any changes to our code to leverage quantum-resistant TLS when available (given that we’re not specifying a different KEM programmatically).
The SSLParameters::setNamedGroups can be used to select a specific schema when configuring a TLS socket connection.
SSLSocket tlsSocket = (SSLSocket)(SSLContext.getDefault().
getSocketFactory().createSocket());
SSLParameters parameters = tlsSocket.getSSLParameters();
parameters.setNamedGroups(new String[] {
"SecP256r1MLKEM768"
});
tlsSocket.setSSLParameters(parameters);|
Note
|
It is important to act now, as attackers are actively harvesting data in preparation for future quantum computing attacks. This is known as the harvest now, decrypt later attack. |
This JEP, which was previously previewed in Java 26 through JEP-524, will allow us to encode/decode 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 BinaryEncodable, 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.
Compared to the previous preview in Java 26 through JEP-524, we see some changes:
-
PEMhas become a Class, rather than a Record. Furthermore, it now includes constructors that accept Base-64 encoded content in byte arrays for convenience purposes. -
DEREncodablehas been renamed toBinaryEncodable. -
EncryptedPrivateKeyInfonow hasgetKeyPairmethods to decrypt PKCS#8-encoded text containing aPublicKey. -
Both the
getKeyandgetKeyPairmethods ofEncryptedPrivateKeyInfothat took a password and Provider now only take a Key. -
The
withFactorymethod ofPEMDecoderhas been renamed towithFactoriesOfto better indicate where the factories are obtained from. -
CryptoExceptionhas been added to indicate failures in cryptographic processing at runtime.
There are some changes that may require us to update our code or configuration when migrating to Java 27 as some options have been removed or renamed. The following are the ones that I found most relevant.
The deprecated -XX:-UseCompressedClassPointers option no longer has any effect as this release removes uncompressed class pointers.
The -XX:InitiatingHeapOccupancyPercent option has been renamed to -XX:G1IHOP to make it clear that it is G1-specific. The old name will still be supported for now, but it’s recommended to update our configuration as soon as possible.
See also the removals under Other Changes, in particular the JVMCI removal.
Not all changes are part of a specific JDK Enhancement Proposal (JEP). If we want to find a full list, I recommend diving into the release notes. I’ve selected a small listing here of the ones that piqued my interest.
According to ISO-8601, the short zone offset format (e.g., +01) is valid, but it was not supported by Java’s date-time API.
This issue has been resolved, and now the short zone offset format is accepted when parsing date-time strings.
For example, the following code will now work without throwing an exception:
ISO_DATE_TIME.parse("2026-06-14T14:30:00+01")
The JNDI/LDAP service provider within the JDK’s java.naming module no longer assigns default values to the java.naming.factory.control, java.naming.factory.object, and java.naming.factory.state standard JNDI properties.
In previous JDK releases, the LDAP service provider automatically configured these properties with class names that weren’t part of the core JDK, which applications often supplied via their classpath. After this release, applications will no longer have access to these factories by default.
So if one of our applications relies on the LDAP service provider to pre-configure these three JNDI properties, we must now explicitly define these values within our own configuration, in accordance with the javax.naming.Context documentation.
The Javadoc tool now recursively copies subdirectories within doc-files directories to the generated output. Consequently, the -docfilessubdirs option, previously required to enable this behaviour, is no longer necessary and may be removed in a future release.
To restore the previous default behaviour, the -excludedocfilessubdir option now supports * as an argument, allowing all doc-files subdirectories to be excluded from copying.
This removes the property, which was deprecated in Java 25, thus reducing the complexity of the codebase.
The jcmd utility now supports Bash autocompletion like many other Linux tools. The script is located at conf/bash-completion/jcmd within the JDK installation directory and can be loaded directly into our shell or installed system-wide.
A new diagnostic command VM.security_properties has been added to retrieve all the enabled security properties. It is the security equivalent of VM.system_properties.
It’s used in the following manner: jcmd <pid> VM.security_properties.
The HashMap.putMapEntries() method has received fast paths for HashMap and Collections.unmodifiableMap(HashMap) resulting in performance gains between 66 and 86 per cent.
Broadly speaking, we can expect the following results:
-
50% faster to add the contents of a
HashMap -
40% faster to add the contents of an unmodifiable map
-
80% faster to add the contents of a
HashMapwrapped in aCollections.unmodifiableMap() -
No detectable regression for other types
The detailed benchmark results can be found in the pull request that implemented this change.
The Arrays.binarySearch methods for primitive arrays have been optimised on x64 architectures using an AVX2 SIMD intrinsic. Instead of the standard scalar search that halves the search space per iteration, the intrinsic executes an 8-ary search using 7 parallel pivot comparisons, shrinking the search range by 8x per iteration.
Key highlights of this change include:
-
Average 1.5x speedup across primitive types for arrays exceeding per-type size thresholds
-
Up to 2.35x peak speedup observed for
intarrays around 1,024 elements -
Zero regression on small arrays: searches below thresholds (e.g., 256 elements for
int, 768 forlong) automatically fall back to standard C2-inlined scalar search
This optimisation is enabled by default on supported x64 hardware and can be disabled with the new
diagnostic flag -XX:-UseAVX2BinarySearchIntrinsic.
9.9. JDK-8382727 - Remove the deprecated -noclassgc, -noverify, -Xverify:none, and -verifyremote options from the java launcher
The -noverify and -Xverify:none options were deprecated in Java 13, -noclassgc and -verifyremote in Java 24 and provided us with warnings if we still used them. The support for these is now being fully removed.
The experimental JVM Compiler Interface (JVMCI) has been removed in this release after more than a decade of experimentation. This includes all associated source code within the HotSpot JVM, the retirement of the jdk.internal.vm.ci, jdk.graal.compiler, and jdk.graal.compile.management modules, as well as the deletion of all JVMCI-related JIT-compilation policies. Additionally, all relevant configure feature flags and any command-line options containing the string JVMCI, such as -XX:+UseGraalJIT have been dropped.
The JVMCI was removed as the ongoing effort required to maintain and test this feature within the JDK outgrew the value it provided to the few projects utilising it. If we depend on it, we’ll need to carry and maintain it in our own downstream tree or use an older JDK release that still includes it.
This release once again brings something for everyone, making solid headway towards the long-term vision of Projects Valhalla, Panama, and Amber.
We gain immediate, code-free performance and memory efficiency out of the box through Compact Object Headers (JEP-534) and standardising G1 as the default garbage collector (JEP-523).
Security receives a timely boost with post-quantum TLS key exchanges (JEP-527) and automatic JFR data redaction (JEP-536).
Combined with steady refinements in Structured Concurrency (JEP-533) and Lazy Constants (JEP-531), Java continues to evolve predictably while keeping developer productivity firmly at the forefront.
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. |