In one sentence
Java 27 reached General Availability on 15 September 2026 (JSR 402) — the first non-LTS release after Java 25 — and ships nine JEPs. There is no dramatic new syntax; the focus is on three things: firming up runtime defaults (G1 as the default GC everywhere, compact object headers on by default), closing a security gap (post-quantum hybrid key exchange enabled by default in TLS 1.3), and continuing to refine a set of preview and incubating features (lazy constants, primitive pattern matching, structured concurrency, the Vector API and PEM encodings).
For teams still on Java 21 / 25 LTS, Java 27 reads more like a preview of where the platform is heading; but two of its Final changes will greet you anyway the next time you upgrade to an LTS.
Positioning: non-LTS, nine JEPs in three buckets
First, the basics. Oracle ships a feature release every six months and an LTS every two years. Java 21 and Java 25 are LTS releases; Java 27 is not, and Oracle will update JDK 27 until March 2027. Production should still prefer an LTS; Java 27 is best used to validate new behaviour in test environments or to try out on new projects.
| Maturity | JEP | Meaning |
|---|---|---|
| Final | 523 · 527 · 534 · 536 | Runtime and security changes that are on by default |
| Preview | 531 · 532 · 533 · 538 | Require --enable-preview to use |
| Incubator | 537 | The Vector API keeps incubating; its API may still change |
Below we go through language, API, performance, tooling/JVM, and then deprecations and removals, closing with a migration checklist.
Language: two features still in preview
No new syntax was finalised this time; the interesting language work is the evolution of two preview features.
JEP 531 · Lazy Constants (third preview)
This is not a language-level lazy keyword, but a preview API — java.lang.LazyConstant. You provide a computation function at declaration time, and the real initialisation is deferred to the first read; it runs at most once under concurrency, the result is immutable, and it must not be null.
// Not initialised at declaration; created on first get()
private static final LazyConstant<Logger> logger =
LazyConstant.of(() -> Logger.create(App.class));
// Lazy collections: elements initialised on demand
static final List<Connection> pool =
List.ofLazy(16, _ -> new Connection());
It targets the "lots of static final fields eager-initialising at startup" problem: ordinary static fields are all evaluated during class initialisation, whereas a lazy constant defers that cost until the value is actually touched. To benefit from the JVM's constant folding, keep the LazyConstant in a final field (ideally static final). Relative to JDK 26, this preview drops the low-level isInitialized() and orElse(T) methods and adds Set.ofLazy(...).
JEP 532 · Primitive types in patterns, instanceof, and switch (fifth preview)
Previously, instanceof, switch and record patterns only worked with reference types. This version lets int, long, float, double, boolean and friends take part in pattern matching, with one central rule: a pattern matches only if the conversion is lossless.
// instanceof with a primitive pattern
int i = 1000;
if (i instanceof byte b) { /* no match: 1000 does not fit a byte */ }
// switch now supports long / boolean, plus guards via when
switch (status) {
case 0 -> "ok";
case int n when n >= 100 -> "high";
case int n -> "unknown: " + n;
}
// a primitive pattern nested inside a record pattern
record JsonNumber(double d) {}
if (json instanceof JsonNumber(int age)) { /* only if double → int is lossless */ }
This removes a lot of "cast manually, then range-check" boilerplate, and lets switch handle long, boolean and floating-point selectors directly. Note that a floating-point case constant must match the selector type — a float selector needs 0f, not 0, or it will not compile.
API updates: concurrency, vectors and cryptography
Nothing new was finalised on the API side, but four APIs continue to mature in preview/incubator, alongside a few small final additions.
- Structured Concurrency (JEP 533, seventh preview):
StructuredTaskScopemanages a set of related tasks as one unit of work, making parent-child exception propagation, cancellation and observability much clearer — a key piece after virtual threads. Still preview; the API is not yet stable. - Vector API (JEP 537, twelfth incubator): a SIMD-oriented API that can compile to CPU vector instructions, aimed at numerical workloads. It has incubated for a long time, so the API may change — do not build production code on it yet.
- PEM encodings (JEP 538, third preview): read and write certificates and keys in standard PEM form, removing the hand-written "BEGIN/END" glue.
- Post-quantum hybrid key exchange (JEP 527, final): TLS 1.3 gains
X25519MLKEM768,SecP256r1MLKEM768andSecP384r1MLKEM1024groups, enabled by default, combining quantum-resistant algorithms with classical ones to defend against "harvest now, decrypt later".
Smaller final updates include KeyStore.getCreationInstant(String) returning an entry's creation time, FFM API support for initialising thread-local execution state before a downcall, new security properties such as jdk.security.password.allowSystemIn, and extra HSS/LMS signature parameter sets.
Performance: G1 everywhere and compact object headers
Two Final changes touch every Java application.
- JEP 523 · G1 as the default GC in all environments: G1 becomes the default collector everywhere, including client/small-memory cases that previously used Serial GC, unifying default behaviour. G1's
MinHeapFreeRatio/MaxHeapFreeRatiodefaults also change to 0/100, so the heap no longer shrinks and grows just to satisfy those constraints. - JEP 534 · Compact object headers: on 64-bit platforms the object header shrinks from 96 to 64 bits (the class pointer is always compressed), saving more the smaller your heap and the more small objects you allocate. Relatedly,
UseCompressedClassPointersis now obsolete — class pointers are always compressed, and setting the flag is ignored with a warning.
For most applications these two defaults are free wins; the libraries to watch are those that depend on object-header layout or do exotic off-heap/bytecode tricks.
Tooling and JVM adjustments
- JFR in-process data redaction (JEP 536, final): command-line arguments, environment variables and initial system-property values are redacted by default, lowering the risk of writing secrets into JFR recordings; it adds
redact-keyandredact-argumentoptions, and thejdk.SystemProcessevent no longer records command-line arguments. - jcmd improvements: Bash completion, plus a
VM.security_propertiescommand to inspect a running JVM's security properties;VM.infoand the fatal-error log now report the number of open file descriptors. - AOT mode: new
-XX:AOTMode=required(an alias foronthat will replace it). - TLS details: TLS 1.3 certificate compression (zlib) is on by default;
ffdhe6144andffdhe8192are removed from the default named groups.
Deprecations and removals: check these before upgrading
This is the part to compare line by line when migrating.
Removed
ThreadPoolExecutor.finalize()(deprecated earlier)- JVMCI: the
jdk.internal.vm.ci,jdk.graal.compilerand related modules and flags - Launcher options
-noclassgc,-noverify,-verifyremote,-Xverify:none - The VFORK process-launch mechanism on Linux and the Serviceability Agent
printmdocommand - The
java.locale.useOldISOCodessystem property (legacy ISO language codes no longer work)
Deprecated / behaviour changes
-XX:InitiatingHeapOccupancyPercentis renamed to-XX:G1IHOP(the old name remains an alias)- The LDAP provider no longer sets three JNDI standard properties by default;
HttpServermoves from string-prefix to path-prefix matching - ML-KEM / ML-DSA private keys now default to
seedencoding, which has interoperability implications with older JDKs TimeZone.getDefault()returns the latest IANA ID on Windows (e.g.Asia/Kolkata);ServiceLoaderthrowsServiceConfigurationErrorconsistently on linkage errors
Migration checklist
- Decide whether it is worth it: production prefers Java 21 / 25 LTS; use Java 27 in a test environment if you just want to validate new behaviour.
- Enable preview explicitly: when using JEP 531/532/533/538, both compilation and runtime need
--enable-preview. - Scan for removals: grep for
ThreadPoolExecutor.finalize, legacy launcher flags and Graal/JVMCI dependencies; if a library depends on JVMCI, confirm a replacement first. - Audit GC and object-header assumptions: make sure no code or monitoring script depends on the old default GC or on the object-header layout.
- Watch cryptographic interoperability: if you exchange ML-KEM/ML-DSA private keys with older versions, note the default encoding is now
seed. - Run the full test suite: focus on serialisation, time zones, launch flags and TLS handshakes — the usual trouble spots.
Looking ahead
Zoom out and the longer-term projects are getting closer: Project Valhalla (value types, cutting pointer-chasing and boxing costs), Project Leyden (startup and warm-up performance), and the steady march of post-quantum cryptography. The repeated previews of structured concurrency and the Vector API also show Java will keep polishing both its concurrency and numerical-computing stories. Java 27's value is not "one more syntax sugar" — it is that it hands developers, ahead of time, a batch of behaviours that are about to become the default.
Summary
Java 27 is a foundation release: G1 and compact object headers operate on every byte of the runtime, post-quantum TLS raises the security baseline, and the language/API side stays preview-heavy for teams willing to experiment. For most developers the most important action this cycle is not to upgrade, but to read the two lists — removals and behaviour changes — so the next LTS upgrade is already planned for.