JDK27新特性深度解读

9月15日,JDK 27 GA 版本(build 27+35)正式上线。Mark Reinhold 的发布公告中一共列出9项JEP,是自JDK 20(7个JEP)以来特性最少的版本;JDK26包含10个JEP,JDK25则多达18个。

一、JDK27 新特性一览

密码学更新

JEP 527:TLS 1.3 后量子混合密钥交换(JDK27最重磅特性)

如果你只关心JDK27里一件事,那必然是它。JEP527 将后量子密码直接内置到TLS握手流程,并且默认启用,业务代码一行不用改。

解决的安全威胁:
能够破解RSA、椭圆曲线DH算法的量子计算机目前还不存在。但“先收集、后解密”(harvest now, decrypt later)威胁早已存在:攻击者可以现在就抓取你的加密流量保存下来,等到未来大型量子计算机问世,再解密这些历史数据。对于医疗记录、涉密信息这类需要长期保密的数据,风险已经开始倒计时。

方案原理:
IETF TLS工作组选定混合密钥交换方案:把久经考验的经典密码算法 + 抗量子算法组合在一起。只要其中一类算法安全,整个链路就是安全的。既防御量子攻击,也承认新型格基密码算法还没有经过数十年密码学的充分检验。

JDK27实现3套混合方案,全部是 ML-KEM + 临时椭圆曲线ECDHE:

- X25519MLKEM768:X25519 + ML-KEM-768
- SecP256r1MLKEM768:secp256r1 + ML-KEM-768
- SecP384r1MLKEM1024:secp384r1 + ML-KEM-1024

这是一条持续多版本演进的路线:

- JDK21(JEP452):新增KEM密钥封装机制API
- JDK24(JEP496):内置ML-KEM算法
- JDK27(JEP527):把这套能力接入TLS1.3握手

使用方式:
TLS客户端在握手阶段按优先级顺序宣告支持的密钥协商组,X25519MLKEM768被放到优先级列表第一位。只要你的代码使用javax.net.ssl,且没有硬编码固定密钥组,会自动启用抗量子TLS握手。

如需手动控制,可以通过系统属性jdk.tls.namedGroups全局覆盖,也可以单连接配置:


SSLSocket tlsSock = (SSLSocket) SSLContext.getDefault()
        .getSocketFactory().createSocket();
SSLParameters params = tlsSock.getSSLParameters();
// 2套混合KEM + 2套传统算法
params.setNamedGroups(new String[] {
    "SecP256r1MLKEM768",
    "X25519MLKEM768",
    "secp256r1",
    "x25519"
});
tlsSock.setSSLParameters(params);

在今年6月,这个特性还存在一个阻碍:底层IETF规范只是草案。但在夏季,规范正式定稿:RFC9954(信息类文档)、RFC10024(标准提案)发布,扫清发布障碍。

验证代码:
下面代码访问Cloudflare后量子测试端点,查看协商使用的密钥交换算法:


public class Kex {
    public static void main(String[] args) throws Exception {
        var uri = URI.create("https://pq.cloudflareresearch.com/cdn-cgi/trace");
        var body = HttpClient.newHttpClient()
                .send(HttpRequest.newBuilder(uri).build(), HttpResponse.BodyHandlers.ofString())
                .body();
        body.lines().filter(l -> l.startsWith("kex="))
                .forEach(l -> System.out.println("JDK " + Runtime.version().feature() + ": " + l));
    }
}

同一套代码,在JDK27运行,握手结果会显示为后量子密钥交换,正是本JEP的核心目标。

JEP 538:密码对象PEM编解码(第三次预览)

继续密码库的升级。JEP538是继JDK25(JEP470)、JDK26(JEP524)之后,PEM编解码API的又一轮预览迭代。

简单解释:密码对象底层是二进制DER格式,机器友好但人无法阅读。PEM(Privacy-Enhanced Mail)把DER用Base64包裹,带上-----BEGIN ...-----头尾标记,就是我们在Git仓库、服务器配置、邮件里常见的密钥文件。在这套API出现之前,Java没有官方原生的PEM读写能力,开发者只能手写解析器或者引入第三方库。

API示例:


String pem = PEMEncoder.of().encodeToString(privateKey);        // 对象转PEM
PrivateKey key = PEMDecoder.of().decode(pem, PrivateKey.class); // PEM转对象

原本计划JDK27直接转正为正式API,但5月收到后期反馈,作者决定再多一轮预览。
API改动要点:

- 通用PEM内容类由record改为普通类,支持通过字节数组传入Base64内容
- DEREncodable 重命名为 BinaryEncodable
- 新增CryptoException
- PEMDecoder.withFactorywithFactoriesOf
- EncryptedPrivateKeyInfo增加getKeyPair方法,getKey/getKeyPair新增支持传入Key而非密码、支持指定Provider的重载

后续路线:JEP542 已经合入JDK28,计划不做改动直接转正。 JDK27预览版编写的代码,升级JDK28后只需要移除--enable-preview,代码无需修改即可编译运行。

内存优化

JEP 534:默认启用紧凑对象头(Compact Object Headers)

JEP534把紧凑对象头升级为HotSpot虚拟机默认对象布局。该特性最早在JDK24作为实验参数,JDK25转为生产可用选项。

原理回顾:
传统64位HotSpot对象头占两个机器字:mark字 + 类指针。当内存中存在海量对象时,第二个字会带来巨大内存开销,增加GC压力、降低CPU缓存命中率。紧凑对象头把mark字、压缩类指针和少量元数据压缩到单个64bit,同时保留hashCode、同步锁等全部原有能力。

亚马逊在数百个生产服务完成大规模验证,证明稳定性达标,因此JDK27默认开启。

- 收益:开箱即用节省堆内存
- 回退:如需切回旧布局,使用 -XX:-UseCompactObjectHeaders

简单测试场景:1000万个包含两个int字段的Record对象

- 旧布局:对象头12字节 + 字段8字节,对齐后24字节
- 紧凑头:对象头8字节 + 字段8字节,对齐后16字节
单对象内存下降,整体节省约29%。> 
> 注意:业务场景很难达到这么高的收益,大数组、字符串几乎无法从更小对象头获益。Billy Korando在JDK27运行时笔记给出典型业务堆节省大约20%。
> 这个关闭参数未来版本计划废弃移除。另外,本次JDK27唯一P1级别bug,只在开启紧凑对象头时触发。

JEP 523:所有环境下G1都作为默认垃圾收集器

JEP523,看起来平淡,但解决了长期以来的GC默认选择不一致问题。

历史:
JDK9起,服务器级机器(≥2核、内存约2GB以上)默认G1;低于该阈值(单核、内存小于约1792MB),HotSpot自动降级为Serial GC。当年在小资源环境下Serial在吞吐量和内存占用更有优势。

现在情况变了:经过多轮迭代优化,G1的原生内存占用持续下降;JDK26 JEP522引入第二卡表,G1吞吐量已经接近Serial。同时G1最大延迟一直优于Serial,因为老年代是增量回收,不会发生长时间STW全堆GC。

JDK27变化:
不手动指定GC时,无论CPU核心数、内存大小,一律使用G1。
你依然可以显式使用 -XX:+UseSerialGC 强制指定Serial,只是隐式自动选择逻辑被移除。

配套:AlwaysActAsServerClassMachine / NeverActAsServerClassMachine 在JDK26标记废弃,JDK27直接移除。如果Dockerfile里还保留这两个参数,JVM会打印日志:Ignoring option ... support was removed in 27.0,程序继续运行,不阻断启动。

可观测性 & 工具

JEP 536:JFR 进程内数据脱敏

JDK Flight Recorder(JFR)会记录命令行参数、环境变量、系统属性。这些信息非常利于排障,但一旦.jfr日志文件外泄,很容易泄露数据库密码、API密钥这类敏感信息(很多密码通过-D参数传入)。

JEP536让JFR在进程内部、写入磁盘之前直接脱敏,敏感信息根本不会写入jfr文件,属于纵深防御能力。

默认内置glob匹配规则:*password*_secret__token__api_key*_credential_等。
针对命令行参数,会同时匹配参数名+后面的值,例如--dbpassword ANOTHER_SECRET_PASSWORD,参数名和密码值都会被脱敏。

示例启动命令:


ACCESS_TOKEN=SECRET_TOKEN java -XX:StartFlightRecording:filename=dump.jfr -Xmx2G \
-Djavax.net.ssl.keyStorePassword=SECRET_PASSWORD \
App --dbpassword ANOTHER_SECRET_PASSWORD

-Xmx2G这类不匹配规则的参数,会原样保留。

自定义配置:


-XX:FlightRecorderOptions:redact-key=xxx
# 也可以从文件加载 @keys.txt
-Xlog:jfr+redact=debug # 打开脱敏调试日志
redact-key=none,redact-argument=none # 关闭默认脱敏规则
-XX:FlightRecorderOptions:help # 查看全部选项


> 原本这一节还有JEP528,但它最终没有进入JDK27,下文单独说明。

预览/孵化特性(继续迭代)

JEP 531:惰性常量(第三次预览)

这个特性名字几经迭代:Computed Constants → StableValue(JDK25 JEP502) → LazyConstant(JDK26 JEP526),现在JDK27进入第三次预览。

核心思想:
线程安全、只计算一次,延迟求值。初始化完成后JVM把它当作真正常量处理,支持常量折叠。实现懒加载,又没有传统同步锁的性能开销。

本次预览改动两点:

1. 移除底层方法isInitialized()orElse(),避免不符合API设计的错误用法
2. 新增Set.ofLazy(...)工厂方法。至此List、Set、Map三大集合都有惰性版本,每个元素包装为LazyConstant,仅在首次访问时初始化。

示例代码:


enum Option { VERBOSE, DRY_RUN, STRICT }
// 每个枚举元素只有首次访问时,才执行Application::isEnabled初始化
static final Set

线程安全,初始化完成支持常量折叠。测试时,首次调用contains(VERBOSE)触发求值;第二次访问不再执行;STRICT直到被访问才会初始化。

目前已有JEP草案计划JDK28继续第四次预览、不修改API。

JEP 532:模式匹配、instanceof、switch支持原生类型(第五次预览)

JEP532,第五轮预览,在JDK27版本和JDK26第四次预览完全一致,没有改动,继续收集真实场景反馈。


switch (x.getYearlyFlights()) {
    case 0 -> "No flights";
    case int i when i >= 100 -> "Gold status!";
    case int i               -> "Regular: " + i + " flights";
}

JEP 533:结构化并发(第七次预览)

JEP533,结构化并发第七轮预览(JDK26是第六次)。结构化并发把子任务组视为一个统一工作单元,统一取消、统一异常传播,基于StructuredTaskScope.open()

本次是JDK25重构之后最大API变更:
StructuredTaskScopeJoiner增加第三个泛型参数,用来定义join()抛出的异常类型。默认策略、allSuccessfulOrThrow()anySuccessfulOrThrow()awaitAllSuccessfulOrThrow()join()失败时抛出ExecutionException,根因是子任务异常。新增重载支持异常映射。
awaitAll()被移除;onTimeout()重命名为timeout(),超时抛出CancelledByTimeoutException

示例:


Response handle() throws ExecutionException, InterruptedException {
    try (var scope = StructuredTaskScope.open()) {
        Subtask  user  = scope.fork(() -> findUser());
        Subtask order = scope.fork(() -> fetchOrder());
        scope.join();   // 任意子任务失败抛出ExecutionException
        return new Response(user.get(), order.get());
    }
}

测试:fetchOrder()抛出IllegalStateException,上层捕获到ExecutionException,cause为原始异常;设置50ms超时,异常cause是CancelledByTimeoutException

JEP543(结构化并发,无预览标记)已经进入Candidate状态,计划JDK28API不变直接转正。

JEP 537:Vector API(第十二次孵化)

JEP537,Vector API第12轮孵化。相比JDK25,除ARM/RISC-V向量数学库SLEEF升级3.6.1→3.9.0,没有大的实现改动。

Vector API目标:用纯Java编写向量计算逻辑,JIT自动编译为CPU原生SIMD指令(AVX、NEON、SVE)。
Vector API转正依赖Valhalla项目的值类型(Value Classes)。JDK28的早期构建版本已经内置值对象预览特性,但Vector API何时迁移、转正还没有确定JEP。

二、没能上车的JEP:JEP528 为什么被移出JDK27

我在6月的文章里还说JEP528大概率能进JDK27,但最终它在版本冻结前主动撤回,延期到JDK28。

JEP528 是什么:进程复活诊断

现有的jcmd只能连接正在运行的JVM。JVM崩溃后,只能拿到hs_err日志、core转储文件,读取core文件依赖Serviceability Agent(SA),这套SA代码老旧、脆弱。

JEP528目标:让jcmd可以读取崩溃进程的core文件,复活崩溃JVM用于事后诊断。原理:启动辅助进程,把core文件内存映射回原始地址,加载libjvm到原地址,复用和运行时一样的诊断命令。
方案可以让57条jcmd命令中的26条,直接在Linux core dump、Windows minidump上执行;macOS支持留到后续版本。

撤回过程复盘

- 2025-10-16:JEP进入Candidate
- 2026-05-15:提案纳入JDK27,几分钟后Mark Reinhold退回Candidate
- 5月20日:再次提议纳入JDK27,征集异议截止5月26日
- 5月21日:作者主动将JEP退回Candidate,修改目标版本为JDK28

PR讨论暴露的难点(openjdk/jdk#31011):
PR一共新增61个文件,约6800行代码,但评审中发现大量风险:

1. 复活逻辑极易被新代码破坏,大量VM代码需要增加“是否处于复活模式”判断,代码侵入面很大
2. 复活时强制解锁处于未知状态的互斥锁,属于未定义行为,风险极高
3. API设计争议:Attach API需要新增重载,用来区分“attach到进程PID”和“attach到core文件”,原有API设计方案被评审否决,作者回滚相关代码
4. 还有musl(Alpine Linux)兼容性、ELF解析器复用等待解决问题


> 关键知识点:Proposed to Target(提议纳入版本)只是提案,不等于最终确认纳入。

现状:PR还在开放,8月合并到master分支。作者计划继续迭代,但JDK28也没有正式纳入该JEP。

三、除9个JEP之外,发布说明里值得关注的变更

1. HotSpot移除JVMCI与Graal JIT支持

JVMCI是允许Java编写的编译器替换C2的接口,Graal JIT、Truffle语言都依赖JVMCI。
尽管亚马逊提出希望推迟删除,经过讨论,OpenJDK在JDK27彻底移除JVMCI。
原因:代码里存在252处#ifdef INCLUDE_JVMCI分支,约1.5%的JDK提交都需要额外考虑JVMCI兼容,维护成本过高,这个实验性接口已经完成历史使命。

影响:
标准OpenJDK JDK27无法运行Graal JIT,也不能在stock版本上跑带Graal编译的Truffle语言。如果需要JVMCI,项目需要自行维护下游分支。GraalVM本身没有JDK27版本,当前基于JDK25,下一站JDK29。

2. 内置HttpServer 路径匹配行为变更

com.sun.net.httpserver.HttpServer旧逻辑:注册上下文/foo,会匹配/foobar前缀。
JDK27改为完整路径段匹配,/foo不再匹配/foobar


> 这是破坏性变更,大量测试工具、内部小型服务使用该内置HttpServer。
> 可通过系统属性sun.net.httpserver.pathMatcher切回旧行为,该属性未来会删除。

3. 新增一批小型API

- Math/StrictMath新增双曲函数:asinhacoshatanh,移植fdlibm实现
- String.encodedLength(Charset):计算字符串编码后的字节长度,无需创建byte[]数组,减少内存分配
- BigDecimal.rootn(int, MathContext):开n次方,补齐pow、sqrt的能力。示例new BigDecimal("27").rootn(3, MathContext.DECIMAL64)得到3

4. jcmd小升级(弥补JEP528缺席)

- Linux:增加bash补全脚本,source $JAVA_HOME/conf/bash-completion/jcmd即可tab补全
- 新增VM.security_properties命令:打印运行中JVM安全属性
- VM.info、hs_err日志:输出当前打开文件描述符数量,方便排查Too many open files崩溃

5. TLS配套改动(JEP527之外)

1. 默认命名组移除ffdhe6144、ffdhe8192:极少被使用,握手开销大
2. X25519算法性能提升:密钥生成提速55%,密钥协商提速51%,抵消混合握手带来的额外开销
3. TLS1.3 zlib证书压缩(RFC8879)默认开启:证书链很长时,减小握手包大小
4. ML-KEM、ML-DSA私钥默认使用seed格式保存在PKCS#8:JDK27生成的后量子密钥,旧版本JDK默认无法读取。跨版本兼容需要设置系统属性jdk.mlkem.pkcs8.encoding=expandedKey

6. Finalization 清理:移除ThreadPoolExecutor.finalize()

ThreadPoolExecutor.finalize()在JDK9废弃,JDK18标记待删除,JDK27彻底移除。
如果你继承ThreadPoolExecutor并且在finalize()调用super.finalize(),现在会调用Object.finalize(),方法签名抛出Throwable,会导致代码编译失败。

7. Oracle与Adoptium不再提供Intel Mac(macOS x64)JDK27安装包

官网JDK27提供:Linux x64/AArch64、macOS AArch64、Windows x64。不再提供Intel Mac版本。


> JEP541计划在JDK28正式废弃macOS x64移植。第三方厂商(如Azul Zulu)依然提供Intel Mac的JDK27构建包。

四、本次发布周边事件

RC延期与本次发布唯一P1严重bug

原定JDK27第一个RC在8月6日,延后到8月20日。
就在RC前,Lorenzo Dematté团队在早期访问版本发现P1级bug:在x64 Linux平台,C2 JIT内联Arrays.copyOfRange时,序列化字符串读取后前4字节损坏;关闭紧凑对象头 -XX:-UseCompactObjectHeaders 即可规避。
根因是build24引入的intrinsic错误内存别名,在RC前一天回退修复。


> 启示:把业务测试套件跑在JDK早期预览版,是提前拦截严重bug的有效手段。

厂商、工具生态支持

- SapMachine 27在9月15日UTC 08:54率先发布;Azul Zulu、Amazon Corretto27同步上线;Temurin27当时还未发布;微软只维护LTS版本,无微软版OpenJDK27
- IntelliJ IDEA 发布首日支持JDK27
- Gradle 9.8.0-RC1:支持JDK27(daemon和toolchains)
- Kotlin 2.5.0-Beta1:新增JVM27字节码目标

相关项目消息

1. JDK28重磅:Valhalla值对象(JEP401)进入主线,预览特性。但早期测试发现null受限扁平值数组下System.arraycopy性能比普通循环慢8~20倍,还需要大量测试。JDK28还包含:默认分代Shenandoah、简单JSON API(孵化)、JEP543结构化并发转正、Leyden项目AOT编译JEP544。
2. JDK-8377715(虚拟线程栈解冻会破坏去优化):该bug在JDK27主线修复,JDK27正式版已包含修复。
3. 祝贺Johannes Bechberger成为Java Champion。



# 升级建议总结

✅ 优先升级理由

1. TLS1.3 默认开启后量子混合密钥交换,零代码改造,防御“先录后解”量子威胁,对安全敏感系统价值极高
2. 默认紧凑对象头,堆内存平均节省约20%
3. 全平台统一G1为默认GC,消除小环境自动切Serial GC的行为差异
4. JFR内置敏感信息脱敏,防止日志泄露密钥密码

⚠️ 升级风险点

1. 内置HttpServer路径匹配规则变更,影响依赖内置HttpServer的小工具
2. ML-KEM/ML-DSA密钥默认存储格式变化,JDK27生成密钥旧JDK默认无法读取
3. JVMCI被移除,使用Graal、Truffle的应用不能直接迁移标准OpenJDK27
4. 不再提供Intel Mac官方包,Intel Mac用户需要选用Azul等第三方发行版