How to Upgrade to JDK 27: G1 Default and Compact Object Headers
JDK 27 changes two defaults that can move your heap numbers and pause times. Here is how to test them before the upgrade reaches production.
On this page 9 sections
To upgrade to JDK 27, test two default changes first: G1 is now the default garbage collector in every environment, and compact object headers are on by default. Run your service under load with both, compare pause times and heap size, then roll out. Most services need only a short benchmark and a flag review.
JDK 27 reached general availability on September 15, 2026. It is a short-term release with six months of support from Oracle, according to InfoWorld's feature rundown. It follows JDK 26, which arrived in March.
This guide is for backend engineers and platform owners who run Java services in containers and want to know what changes before they bump a version. It covers the two default changes, the preview features, and a test plan. Backtrace writes for engineers who inherit these upgrades, so the focus here is the checks that save you a bad deploy.
What JDK 27 ships
The OpenJDK project page for JDK 27 lists nine JEPs, which are the JDK Enhancement Proposals that define each change. Two alter defaults, one adds post-quantum cryptography, and the rest are preview or incubating features.
Here is the full list in plain terms:
| JEP | Change | Status |
|---|---|---|
| 523 | G1 is the default garbage collector everywhere | Final |
| 527 | Post-quantum hybrid key exchange for TLS 1.3 | Final |
| 534 | Compact object headers by default | Final |
| 536 | JFR in-process data redaction | Final |
| 531 | Lazy constants | Third preview |
| 532 | Primitive types in patterns, instanceof and switch | Fifth preview |
| 533 | Structured concurrency | Seventh preview |
| 537 | Vector API | Twelfth incubator |
| 538 | PEM encodings of cryptographic objects | Third preview |
Only the first four affect a normal production service without extra flags. The rest need an explicit opt-in and should stay in experiments.
Why does the G1 default matter for containers?
The G1 default matters because small containers used to land on a different collector. JEP 523 makes G1 the default in all environments, so HotSpot always selects it when you name no collector. Teams running tiny pods may see new pause and memory behavior after upgrading.
The stated goal of JEP 523 is to make G1 the choice without a significant drop in throughput, latency, memory footprint or startup time. That is a goal, not a guarantee for your workload. A service with a tiny heap and a strict startup budget deserves a direct test.
Here is the practical risk. If you never set a collector, you have been using whatever the JVM picked for your machine shape. After the upgrade, the pick changes for some shapes. Your old tuning flags may no longer mean what you think.
Look at your launch scripts and container specs for options that start with the collector name, such as heap region sizes or pause targets. Remove the ones nobody can explain, and keep the ones backed by a graph. Backtrace's editors suggest writing the reason next to each flag as you go.
Startup behavior is worth a look too. Our guide to JVM startup time and cold starts explains how collector and memory choices show up in serverless and autoscaled services.
What compact object headers change
Compact object headers shrink the header on every Java object from 96 bits to 64 bits on 64-bit architectures, per the InfoWorld summary of JEP 534. Less overhead per object means a smaller heap, better density and better data locality.
The gain depends on your data. A service that holds millions of small objects, such as cache entries, small DTOs or boxed values, benefits the most. A service built around large byte arrays or direct buffers sees little change.
This also affects capacity planning. If a smaller heap lets you lower container memory requests, you can run more pods per node. Our Kubernetes cost optimization guide covers how to turn a memory saving into a real bill reduction instead of idle headroom.
Do not assume the saving. Measure it, because a headline percentage from a benchmark will not match your object mix.
Step-by-step: test the upgrade
Follow these steps in a staging environment that mirrors production load:
- Record a baseline on your current JDK: p50 and p99 latency, GC pause totals, heap after full collection, and container memory.
- Start the same build on JDK 27 with no collector flags, and capture the same numbers.
- Repeat with your current collector flags if you have any, to see whether they still help.
- Run the test at least twice, once warm and once cold, to catch startup differences.
- Compare container memory use and decide whether requests can drop.
Keep the test boring. Use real traffic replays if you have them, and a steady load generator if you do not. Change one thing at a time so you know what caused a difference.
Record results in the same dashboard you use for production. If your pipeline lacks that visibility, start with our explainer on data pipeline observability basics, which covers the habit of watching the signals before a change.
Add Java Flight Recorder to the run. JEP 536 adds optional in-process redaction of command-line arguments, initial environment variable values and system property values from recordings before they leave the process. That makes sharing a recording with a vendor or a colleague much safer.
Roll out and watch the right signals
Once staging looks good, roll out in stages. Start with one replica of one service, and let it take real traffic for a full business cycle. Compare it against its siblings still running the old JDK.
Watch four signals during the canary period: p99 latency, total GC pause time per minute, heap after collection and container memory working set. A rise in any of them is a reason to stop and read the logs, not to push on.
Also watch restarts. A service that now gets killed for exceeding its memory limit has probably changed its off-heap or metaspace use, which is a separate setting from the heap. Raise the limit only after you understand where the memory went.
Who should wait on the upgrade
Some teams should hold. If you run a long-term support release for compliance reasons, stay there and test JDK 27 on the side. A short-term release gets six months of support, so you would need to upgrade again soon.
Teams with heavy native dependencies should also test first. Libraries that poke at object layout, such as some serialization, profiling or off-heap tools, may assume a header size. Read each vendor's release notes and run their test suites on the new JDK before you commit.
Finally, keep a rollback path. Keep the old image tagged and deployable, and write down the single flag or version change that reverts the service. A rollback you have rehearsed takes minutes. One you have only described can take hours.
Post-quantum TLS and what to do about it
JEP 527 adds hybrid key exchange for TLS 1.3. It combines a quantum-resistant algorithm with a traditional one, so a future quantum computer cannot easily read recorded traffic. The InfoWorld article does not name the specific algorithms, so check the JEP before you document your configuration.
For most teams, the action is small. Confirm your TLS stack negotiates cleanly after the upgrade, and watch for older load balancers or proxies that mishandle larger handshake messages. Test an end-to-end call through every hop, including legacy ones.
Treat this as a safety margin, not an emergency. It is worth knowing which services carry long-lived sensitive data, since those are the ones attackers could record now and decrypt later.
Should you use the preview features?
Mostly not in production. Structured concurrency is in its seventh preview, primitive patterns are in their fifth, and the Vector API is in its twelfth incubation. Each one can change again, and each needs a flag to compile and run.
That said, experiments are cheap. Structured concurrency is the one most backend teams should try on a side project, because it changes how you reason about cancelation and errors across threads. Keep any preview code out of shared libraries, so an API change does not ripple to other teams.
For a broader view of how a language upgrade checklist looks, compare this with our Go 1.27 upgrade checklist. The shape is similar: defaults first, previews last.
Conclusion
JDK 27 is a short-term release with two defaults that can change real numbers. Test the G1 default and compact object headers under load, review any collector flags nobody can explain, and keep preview features in experiments.
Start with a baseline run this week. If the numbers hold, you can plan the rollout with confidence. If they do not, you will know exactly which default to pin before the change reaches production.