Performance Improvements in JDK 27
JDK 27 continues the steady cadence of performance work across the JDK. More than 2,300 commits have landed in OpenJDK since JDK 26, including changes to the libraries, garbage collectors, JIT compiler, and runtime.
One change in particular will have especially broad reach: The "Compact Object Headers" feature is now enabled by default. Other broader changes include making G1 the default garbage collector everywhere.
Unless otherwise noted, the numbers below come from JMH benchmarks or workload measurements published with the corresponding pull requests. They show the local effect of a change, not its impact on an entire application; hardware, data shape, heap sizing, garbage collector, warmup, and compilation state all matter.
JDK Library Improvements
Lazy Constants Return for a Third Preview
JEP 531 previews Lazy Constants for a third time. A LazyConstant<T> computes its value on first access and can be initialized only once. The JVM can trust that binding much like a final field, enabling optimizations such as constant folding without eager initialization or a manual double-checked-locking scheme.
JDK 27 removes the low-level isInitialized() and orElse() methods, whose use could undermine the intended initialization model, and adds Set.ofLazy(...) to the existing lazy list and map factories. As a preview API, Lazy Constants still require --enable-preview.
Faster HashMap Bulk Copies
HashMap.putAll() and the HashMap(Map) constructor previously copied entries using the Map interface. That path creates an iterator and invokes methods such as next(), getKey(), getValue(), and hashCode(). When a call site sees several Map implementations, these calls can become megamorphic and difficult for the JIT compiler to optimize; an unmodifiable wrapper adds another layer of virtual calls.
JDK-8371656 (PR #28243) adds specialized paths when the source is exactly a HashMap or an unmodifiable map backed by one. The implementation walks the source table directly and reuses the hash stored in each node, avoiding iterator machinery and repeated hash-code calculation.
On AWS Graviton, with call sites deliberately exposed to five Map types, the reported operation-time reductions ranged from 61% for a five-entry HashMap to 86% for an unmodifiable 150-entry HashMap. In the latter case, the benchmark fell from about 10,593 ns/op to 1,533 ns/op. Other source-map types continue to use the general path.
Faster Attributed-Text Processing
Applications that perform text layout (for example in Swing GUIs) may spend surprising amounts of time in AttributedString. Its run attributes were represented by two arrays of Vector instances, so looking up an attribute for each character could require a linear Vector.indexOf() search.
JDK-8380794 (PR #30402) replaces that structure with one array of maps. In the submitted benchmark, iteration with one or more attributes took 35% to 40% less time. Creating a string with one attribute also allocated about 20% less memory; the difference was about 1% with four attributes and negligible with none. Code built on AttributedString, including text measurement and line-breaking APIs, can benefit.
More Efficient URL Formatting
URL.toString() delegates to URLStreamHandler.toExternalForm(), which also appears in startup and class-loading profiles. The old implementation built some components as intermediate concatenated strings and did not specialize for the common case in which optional components are absent.
JDK-8379557 (PR #30151) flattens the concatenation and adds fast paths for common URL shapes. Depending on which authority, query, and fragment components were present, the submitted benchmarks took at least 60% less time. Interpreter-only tests retained the improvement, showing that it does not depend solely on compiled execution.
Eliminated System Call When Opening Files
On Unix, FileOutputStream and RandomAccessFile used to call fstat after opening a file for writing to reject directories. Because the relevant open operation already fails with EISDIR, the extra system call was redundant.
JDK-8373647 (PR #28823) removes that call. A stress benchmark fell from about 3,722 ns/op to 3,192 ns/op, a 14% reduction. The application-level gain depends on how often files are opened rather than reused, but the change removes a kernel transition from a common API path.
Cryptographic Acceleration
JDK 27 includes several architecture-specific and algorithmic improvements to cryptographic code:
JDK-8376164 (PR #29385) processes a complete AES/ECB message in an x86 intrinsic and adds round keys in parallel instead of entering a native stub once per 16-byte block. On an Intel Core i9-14900HX, the submitted benchmark improved from about 11.52 µs/op to 8.38 µs/op, or roughly 37% higher throughput.
JDK-8384353 (PR #31125) adds an AVX2 SHA-3 intrinsic and increases parallelism in the AVX-512 implementation. Submitted benchmarks improved SHA-3 message-digest performance by 16% to 39% with AVX2 and 24% to 33% with AVX-512. ML-DSA and ML-KEM operations built on SHA-3 also benefited. A follow-up, JDK-8386911 (PR #31648), removed unnecessary no-op Keccak calls and fixed regressions on configurations without the new intrinsic.
JDK-8383635 (PR #30991) improves the number-theoretic-transform multiplication used by ML-KEM. The submitted results show 9% to 17% improvements in encapsulation and decapsulation across the three key sizes.
The RISC-V port adds intrinsics for AES/CTR (JDK-8365732, PR #25243), AES/CBC (JDK-8371968, PR #28320), and GHASH (JDK-8373069, PR #28548). On AArch64, JDK-8371459 (PR #28515) selects a general-purpose-register SHA-3 intrinsic on processors where it is faster.
These gains are necessarily hardware-sensitive. HotSpot selects intrinsics according to the processor's reported capabilities, so evaluate them on the machines that will serve the workload.
More Library Improvements
JDK-8378698 (PR #29920) lets
Base64.Encoder.encodeToString()use an internal trustedStringconstructor, avoiding an array copy.Two changes close much of the performance gap between
CharsetEncoder.canEncode(CharSequence)andcanEncode(char)for common built-in charsets: JDK-8376226 (PR #29391) and JDK-8381015 (PR #30456).JDK-8375580 (PR #29288) and JDK-8376403 (PR #29430) remove early
ArrayDequeuse fromURLClassPathandZipFile, respectively.JDK-8376477 (PR #29442) avoids loading empty lock-helper classes used by
ShutdownandReferenceQueue.JDK-8381710 (PR #31142) avoids lambdas in
ModuleBootstrapduring AOT training, reducing incidental class loading.
The library improvements above do not guarantee a better aggregate score. In our testing, minimal applications start in roughly the same time—or slightly more slowly—on JDK 27 than on JDK 26, while using up to 1 MB less memory.
Garbage Collection
G1 Is Now the Default Everywhere
HotSpot has selected G1 by default on server-class machines for many releases, while it could still select Serial GC when running in small or constrained environments. JEP 523 makes G1 the default everywhere in JDK 27.
This is primarily a consistency change, not a claim that G1 suits every workload. It is especially relevant to small containers, single-processor environments, and development machines that previously fell outside the server-class heuristic. Those deployments may see different pause times, CPU use, and memory overhead even when the application is unchanged. Serial GC remains available with -XX:+UseSerialGC.
A supporting change, JDK-8367993 (PR #28723), postpones most G1 concurrent-mark initialization until it is needed. Statistics buffers and concurrent-mark threads no longer have to be created during the earliest phase of VM startup. The submitted results show a small reduction in VM creation time—now relevant more broadly because G1 is the universal default.
Avoiding Pathological GC Behavior
Fixes for costly corner cases may not move typical benchmark scores, but they can be crucial in production:
JDK-8358342 (PR #31610) addresses poor scaling in G1's per-region code-root sets. Users had reported pauses longer than 90 seconds once the structure became expensive to scan and grow.
JDK-8377561 (PR #29673) fixes a Parallel GC feedback loop in which large allocations could trigger repeated Full GCs without enough heap expansion to make progress.
JDK-8379846 (PR #31189) makes G1's adaptive initiating-heap-occupancy calculation less sensitive to a single allocation-rate outlier, which could otherwise trigger endless concurrent cycles. JDK-8381006 (PR #30625) corrects the same calculation when periodic GCs are enabled.
Generational Shenandoah also continues to mature. JDK 27 improves old-generation evacuation (JDK-8376839, PR #29511); removes unnecessary per-object registration and card-alignment work from promotion buffers (JDK-8385606, PR #31316); and allows safepoint preemption while allocating very large arrays (JDK-8379531, PR #30567).
(节选)
原文:Inside.java|Performance Improvements in JDK 27,2026-09-28

成为第一条评论!
Nobody's responded to this post yet. Add your thoughts and get the conversation going.