JDK 27 shipped on September 15. Mark Reinhold posted the general availability notice to the OpenJDK announce list: build 35, the same bits as the second release candidate from August 20. Nine JEPs, four of them previews, one incubator. It is a feature release with six months of support from Oracle. Java 25 stays the LTS version until Java 29 arrives in September 2027.

Most shops on 21 or 25 will skip it. That is the sensible default for a release outside the LTS line. But two of the nine JEPs change what the JVM does when nobody passes it any flags, and untuned JVMs are the majority of JVMs running in the world. Build agents, sidecars, CLI tools, the Jenkins box someone set up in 2019 and forgot. Those processes pick up new defaults first, because they run whatever the base image ships.

G1 is now the collector on every machine

Since JDK 9, HotSpot picked a garbage collector by checking whether it was on a "server-class machine": at least two processors and at least 1792 MB of memory. Pass that test and you got G1. Fail it and you got Serial, the oldest collector in HotSpot, which runs on a single thread and stops the world for every collection. A container with one CPU and a 1 GB limit failed the test. So did most Lambda functions and most CI runners.

JEP 523 deletes that check. G1 is the default everywhere, regardless of CPU count or memory. The rationale in the JEP is that G1's peak throughput is now close to Serial's, its native memory footprint is comparable after JEP 522 in JDK 26 cut synchronization overhead, and its incremental collections give it better pause times than Serial's full collections at any heap size. The JEP includes no benchmark numbers. It states the claim and moves on.

I believe the claim for most workloads. I don't believe it for all of them. The JEP says so itself: "some applications in constrained environments will still perform best with Serial." If you run small JVMs in tight containers and have never set a collector, you have been running Serial for years without knowing it, and after this change you will be running G1 without knowing it. G1 uses more threads and more memory outside the heap for its remembered sets and card tables. In a container with a hard memory limit, that native memory comes out of the same cgroup budget as your heap. Watch for OOM kills that weren't there last week.

The fix is one flag. An explicitly chosen collector always wins over ergonomics, so UseSerialGC puts things back. Set it on purpose, in the config, rather than relying on a machine detection rule the JDK no longer has.

Object headers shrink from 96 bits to 64

The second default change is compact object headers, JEP 534. Every object on a 64-bit HotSpot heap has carried a header of 12 bytes: a mark word of 8 bytes and a compressed class pointer of 4. Project Lilliput squeezed the class pointer into the mark word, and the header is now 8 bytes. That is 4 bytes off every object you allocate, and most Java objects are small, so the saving is a large fraction of the heap.

The feature arrived as experimental in JDK 24, became a supported product option behind a flag in JDK 25, and is now on by default. The JEP's numbers from the period when it was optional: SPECjbb2015 used 22% less heap and 8% less CPU time. G1 and Parallel ran 15% fewer collections. A JSON parsing benchmark finished in 10% less time. Those are the gains you normally pay for with a hardware refresh, and here they come from a memory layout change nobody has to think about.

The risk is anything that assumes the old layout. Agents that poke at raw object memory, and tools that compute object sizes by hand. The JEP reserves four header bits for Valhalla's value objects, which are proposed for a preview in JDK 28, so the layout will keep moving. The old 96-bit header is still available by turning the flag off, with a note that it will be deprecated in a future release. If you run JOL or a custom allocator in production, test before you trust.

Post-quantum TLS and a Flight Recorder that redacts secrets

JEP 527 adds three hybrid key exchange groups to the TLS 1.3 stack: X25519MLKEM768, SecP256r1MLKEM768, and SecP384r1MLKEM1024. Each pairs a classical curve with ML-KEM, the lattice scheme NIST standardized in 2024. X25519MLKEM768 goes to the front of the default list of named groups, ahead of plain x25519, and it is one of only two groups for which the client sends a key share in its first flight. Every Java 27 client that opens an HTTPS connection with default settings will lead with a post-quantum offer.

This matches what Chrome, Firefox, Cloudflare, and OpenSSL 3.5 have done since 2024 and 2025, so most current servers will accept it. The failure mode is the one Chrome hit in 2024. An X25519MLKEM768 key share is 1,216 bytes, which pushes the ClientHello past a single packet, and some middleboxes and old TLS terminators drop or hang on a split hello. If you have a Java service behind an appliance that hasn't been patched in a while, that is where to look when connections start timing out after the upgrade. The group order is configurable through the jdk.tls.namedGroups property or SSLParameters.setNamedGroups, so you can fall back to classical groups per service.

JEP 536 is the small one that will save somebody's job. Java Flight Recorder captures command line arguments, environment variables, and system properties at startup, because that data helps when you are diagnosing a crash. It is also where people put passwords. A keystore password in a system property, or an ACCESS_TOKEN in the environment. Both landed in the .jfr file, and .jfr files get attached to support tickets and dropped in shared buckets.

In JDK 27, JFR redacts inside the process before anything is written. The default filters match keys and arguments containing password, token, secret, credential, and api key, case insensitive, and replace the value with [REDACTED]. You can extend the list with your own globs, load them from a file, or switch it off with redact-key=none if you have a reason. The one gap the JEP admits: if the JVM crashes hard, unredacted data can remain in temporary files. Good enough. Turning it on by default was the right call, and I'd like to see the same idea applied to heap dumps next.

Skipping the release still means testing the defaults

Don't move production to 27 if you're on 25. Six months of support is not a lifecycle, and 28 lands in March 2027 with its own default changes, including a proposal for generational Shenandoah by default and a deprecation of the macOS x64 port. But do run your test suite on 27 now. The two default changes will not be reverted in 29, and 29 is the LTS you will end up on. Better to learn in a CI job that your memory limits were sized for Serial than to learn it during the migration.

Two things to add to your JVM config this week, whatever version you run:

  • Set the collector explicitly. If you want Serial in small containers, say so. If you want G1, say so. Ergonomics has changed twice now, in 9 and in 27, and it will change again.
  • Set the heap limit explicitly in every container, and leave headroom for native memory. The JDK has been telling people this for years, and the G1 switch makes it matter more.
-XX:+UseSerialGC            # or -XX:+UseG1GC, but pick one
-XX:MaxRAMPercentage=70.0   # leave room for GC and metaspace
-XX:-UseCompactObjectHeaders  # only if a tool breaks, and file the bug

The preview features are the same ones as in 26 with another number on the end. Structured concurrency is on its seventh preview, primitive patterns on its fifth, lazy constants on its third, and the Vector API on its twelfth incubator. Whether structured concurrency finalizes in 28 or 29 is anyone's guess, but seven previews of one API means the shape is nearly done, and code written against the 27 preview will mostly survive.

The defaults deserve more attention than the version number. A release that changes heap layout and the garbage collector for every untuned JVM is a bigger operational event than most LTS releases, and it will show up in your base images whether or not you asked for it.