前言
2026年9月15日,Oracle正式发布了JDK 27,对应JSR 402,是Java SE 27的参考实现。
说实话,每次Java发布新版本,我都会习惯性看一眼JEP列表,然后判断“这个版本值不值得升级”。
很多版本是“预览版加预览版”,真正能落到生产环境的东西不多。
但JDK 27不太一样。
它包含9项足以单独成为JEP的增强,其中4项是预览功能、1项是孵化器功能。
更重要的是,其中有几项是默认行为变更——也就是说,你什么都不做,升级JDK就能受益。
今天这篇文章,我就把JDK 27的核心变化从头到尾给你拆解一遍。
希望对你会有所帮助。
更多项目实战在Java突击队网:susan.net.cn/project
一、JDK 27到底变了什么?
在深入每个特性之前,我先用一张表帮你建立整体认知。
| JEP编号 | 特性名称 | 类型 | 核心影响 |
|---|---|---|---|
| JEP 523 | G1成为所有环境的默认GC | 正式 | 启动更快,延迟更低 |
| JEP 534 | 紧凑对象头成为默认 | 正式 | 对象头从96位降到64位 |
| JEP 527 | TLS 1.3后量子混合密钥交换 | 正式 | 默认启用,无需改代码 |
| JEP 531 | 惰性常量(第三预览) | 预览 | AI/数据应用的性能优化 |
| JEP 532 | 原始类型模式匹配(第五预览) | 预览 | switch/instanceof支持int/long等 |
| JEP 533 | 结构化并发(第七预览) | 预览 | 并发编程更简单、更可靠 |
| JEP 537 | Vector API(第十二孵化) | 孵化 | AI推理/科学计算向量加速 |
| JEP 536 | JFR进程内数据脱敏 | 正式 | 敏感信息自动脱敏 |
| JEP 538 | PEM编码API(第三预览) | 预览 | 密钥/证书编解码标准化 |
这9项JEP,我按“对普通开发者影响程度”从高到低排列,逐一拆解。
二、G1成为所有环境的默认GC
这是最大的变化。
有些小伙伴在工作中可能遇到过这样的场景:写了个小工具、跑了个批处理脚本,启动的时候发现用的是Serial GC——单线程回收,启动快但一旦数据量上来就卡得不行。你以为是代码问题,其实是GC选错了。
从JDK 9开始,G1就已经是服务器环境的默认GC。
但如果你在一个内存受限的环境里跑Java——比如小容器、嵌入式设备、或者一个简单的命令行工具——JVM会默认选择Serial GC。
Serial GC的问题很明显:单线程,Full GC时会长时间停顿。
在小内存场景下,你可能感觉不到;但一旦数据量稍微大一点,停顿时间就会变得不可接受。
JDK 27的JEP 523把这件事改了:G1成为所有环境的默认GC,不再区分服务器和受限环境。
2.1 为什么敢这么做?
Oracle在JEP里给出了明确的理由:经过这些年的持续优化,G1在所有指标上都已经和Serial持平了。
具体来说:
吞吐量:JDK 27中减少了G1的同步开销(JEP 522),G1的最大吞吐量已经接近Serial。
延迟:G1在老年代回收时走的是增量回收,而不是Serial那种全量回收,所以最大延迟一直比Serial好。
原生内存:最近几个版本把G1的原生内存占用降到了和Serial相当的水平。
启动时间:在小堆场景下,G1的启动开销已经优化到不再明显。
2.2 对你意味着什么?
什么都不用改。
你升级到JDK 27,不指定GC参数,JVM自动用G1。
如果你之前在小内存环境里被Serial GC的Full GC停顿折磨过,升级JDK 27后这个问题会自动消失。
当然,如果你需要极致启动速度,仍然可以显式指定Serial:-XX:+UseSerialGC。
但默认情况下,G1已经是更好的选择。
三、紧凑对象头成为默认
对象头从96位砍到64位。
这是另一个默认行为变更,而且是“悄悄生效”的那种。
3.1 对象头里到底存了什么?
Java对象在堆里是怎么存的?
每个对象都有一个“对象头”,里面至少包含Mark Word和Klass Pointer两部分。
在64位JVM上,传统布局是:
- Mark Word:64位(哈希码、GC年龄、锁状态等)
- Klass Pointer:64位(指向类元数据)
对象头总共96位,也就是12字节。
加上对象体,一个最简单的new Object(),在64位JVM上实际占用16字节(对象头12字节 + 对齐填充4字节)。
3.2 紧凑对象头怎么做的?
JEP 534把Klass Pointer从64位压缩到了32位,对象头总共变成64位,即8字节。
为什么能压缩?
因为JVM的类元数据空间(Metaspace)通常不会超过4GB(32位地址空间足够寻址)。
Klass Pointer其实只需要存一个索引,不需要完整的64位地址。
效果:new Object()从16字节降到12字节(8字节对象头 + 4字节对齐填充)。堆占用直接减少25%。
3.3 为什么这很重要?
堆占用减少25%,意味着:
- 同样的内存能装更多对象
- GC压力更小(对象少了,扫描和回收的负担就小了)
- 数据局部性更好(对象更紧凑,CPU缓存命中率更高)
对于大规模Java应用——特别是那些创建大量小对象的场景——这是一个实打实的性能提升。
紧凑对象头从JDK 24开始就是可选功能,经过两个版本的验证,JDK 27正式把它设为默认。
和G1一样,你不需要做任何事,升级就生效。
四、TLS 1.3后量子混合密钥交换
为“量子时代”提前上锁。
“量子计算离我还远着呢,跟我有什么关系?”
这个问题我被问过不止一次。我的回答是:等量子计算能破解RSA的那一天,你今天的加密数据可能已经被存了好几年了。
攻击者的策略叫“先存后破”——现在把加密流量存下来,等量子计算机成熟了再解密。所以后量子加密不是“未来的事”,是“现在就要做的事”。
4.1 JEP 527做了什么?
JDK 27为TLS 1.3引入了后量子混合密钥交换算法。
所谓“混合”,就是把抗量子算法和传统算法结合起来——即使量子计算破解了传统算法那一半,抗量子算法那一半仍然安全。
新增的算法包括:
- X25519MLKEM768(默认组列表中优先级最高)
- SecP256r1MLKEM768
- SecP384r1MLKEM1024
4.2 关键点:默认启用,无需改代码
如果你用的是javax.net.ssl API,升级JDK 27后,这些算法默认就会生效,不需要修改任何代码。
这意味着:你现有的HTTPS通信,在升级JDK 27后,自动获得了后量子加密保护。
五、惰性常量
它是AI和数据应用的性能利器。
惰性常量(Lazy Constants)是第三次预览了,但这个特性的价值值得反复说。
5.1 它解决什么问题?
Java里传统的static final常量在类加载时就必须初始化。
如果你的常量需要昂贵的计算——比如从数据库加载配置、解析一个大文件、初始化一个ML模型——那类加载就会变得很慢。
惰性常量允许你延迟初始化,而且JVM会把它当成真正的常量来优化——性能等价于final字段。
5.2 代码示例
// 传统的static final——类加载时就得算
public static final List<Config> CONFIGS = loadFromDatabase();
// 惰性常量——用到的时候才算
private static final LazyConstant<List<Config>> CONFIGS =
LazyConstant.of(() -> loadFromDatabase());
// 第一次调用时初始化,之后直接走缓存
public List<Config> getConfigs() {
return CONFIGS.get();
}
为什么这跟AI有关?
因为AI应用里大量存在这种场景——模型权重、tokenizer词表、向量索引,都是初始化昂贵但后续只读的数据。
惰性常量让这些数据可以按需加载,同时不牺牲运行时的性能。
六、原始类型模式匹配
switch终于支持int了。
这是第五次预览,但每预览一次,就离正式版更近一步。
6.1 解决了什么痛点?
以前的switch只能匹配引用类型(String、enum、包装类)。
你要匹配int,只能用传统的switch-case,没法用模式匹配的威力。
JEP 532允许原始类型用于模式匹配、instanceof和switch。
6.2 代码对比
// 以前:int匹配只能这样写
switch (statusCode) {
case 200:
return "OK";
case 404:
return "Not Found";
default:
return "Unknown";
}
// JDK 27预览:可以用模式匹配了
Object obj = getStatusCode();
return switch (obj) {
case int i when i == 200 -> "OK";
case int i when i == 404 -> "Not Found";
case String s -> "String: " + s;
default -> "Unknown";
};
同时,这个JEP还加强了switch的支配性检查,让编译器能在编译期发现更多错误。
七、结构化并发
结构化并发第七次预览了。
虽然还是预览,但它的思路值得每个Java开发者关注。
7.1 传统并发的问题
// 传统写法:两个任务并发,但错误处理一团糟
Future<User> userFuture = executor.submit(() -> fetchUser(id));
Future<Order> orderFuture = executor.submit(() -> fetchOrder(id));
try {
User user = userFuture.get();
Order order = orderFuture.get();
return new Result(user, order);
} catch (Exception e) {
// 一个失败了,另一个还在跑,怎么取消?
// 线程泄漏怎么办?
throw e;
}
问题在于:这两个任务的生命周期没有和父任务绑定。
父任务失败了,子任务可能还在跑;子任务失败了,父任务不知道该怎么取消另一个。
7.2 结构化并发的写法
try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
Supplier<User> user = scope.fork(() -> fetchUser(id));
Supplier<Order> order = scope.fork(() -> fetchOrder(id));
scope.join(); // 等待所有子任务
scope.throwIfFailed(); // 任一失败则抛出
return new Result(user.get(), order.get());
}
// 离开try块时,scope自动关闭,所有未完成的子任务自动取消
核心价值:子任务的生命周期严格嵌套在父任务内。父任务失败,子任务全部取消;子任务失败,父任务感知并处理。没有线程泄漏,没有孤儿任务。
八、Vector API
它是AI推理的加速器。
Vector API第十二次孵化了。
虽然还在孵化阶段,但它在AI推理和科学计算领域的价值已经非常明确。
Vector API允许开发者在支持的CPU上利用SIMD指令——一条指令同时处理多个数据。
对于矩阵运算、向量相似度计算这类AI推理中高频出现的操作,性能提升可以是数量级的。
// 向量加法——一次处理多个float
FloatVector a = FloatVector.fromArray(SPECIES, arr1, i);
FloatVector b = FloatVector.fromArray(SPECIES, arr2, i);
FloatVector c = a.add(b);
c.intoArray(result, i);
如果你的AI应用跑在支持AVX-512或ARM SVE的CPU上,Vector API能把这些硬件的向量计算能力直接暴露给Java代码。
九、其他值得关注的改进
除了9项JEP,JDK 27还有几十项非JEP改进:
JEP 536:JFR进程内数据脱敏——JDK Flight Recorder在记录离开进程前,对命令行参数、环境变量和系统属性进行脱敏。生产环境做性能分析时,不会再意外泄露密钥。
ML-KEM/ML-DSA私钥编码更新——后量子密码算法的密钥编码标准化,X25519和Ed25519性能提升。
JSON线程转储——线程转储中的线程标识、线程数和进程标识改为JSON数字,监控工具解析更方便。
jcmd VM.security_properties——新增命令,可在运行时查看活动的安全属性。
移除JVMCI——部分旧选项和功能被移除。
十、优缺点
优点
1. G1全场景默认,小内存环境不再受Serial GC折磨
吞吐量、延迟、内存占用、启动时间全面持平甚至优于Serial,默认用G1是更安全的选择。
2. 对象头砍到64位,堆占用降低25%
同样的内存能装更多对象,GC压力更小,数据局部性更好。大规模应用直接受益。
3. 后量子加密默认启用,无需改代码
用javax.net.ssl的应用自动获得抗量子攻击能力。这是安全层面的基础性升级。
4. 惰性常量为AI/数据应用优化
模型权重、向量索引等昂贵初始化数据可以按需加载,运行时性能等价于final。
5. 结构化并发让并发编程更可靠
子任务生命周期严格嵌套,没有线程泄漏,没有孤儿任务。
6. JFR数据脱敏,生产环境更安全
性能分析时不会意外泄露密钥和环境变量。
注意事项
1. 非LTS版本,生产环境需谨慎
JDK 27不是LTS,Oracle更新到2027年3月。生产环境建议用JDK 25 LTS。
2. 预览/孵化特性需要--enable-preview
惰性常量、原始类型模式匹配、结构化并发、Vector API都需要显式启用预览标志,且可能与未来版本不兼容。
3. JVMCI被移除
如果你用了Graal JIT编译器(基于JVMCI),需要确认兼容性。
4. 紧凑对象头可能影响某些诊断工具
依赖对象头布局的工具(如某些Profiler)可能需要更新。
十一、适用场景
| 场景 | 推荐程度 | 理由 |
|---|---|---|
| 尝鲜/学习/个人项目 | ✅✅✅ 强烈推荐 | 默认变更直接体验,预览特性可以提前探索 |
| 小内存容器/嵌入式 | ✅✅✅ 强烈推荐 | G1默认+紧凑对象头,启动和内存都受益 |
| 大规模Java应用 | ✅✅ 推荐 | 堆占用降低25%,但需评估非LTS风险 |
| 安全敏感应用(HTTPS) | ✅✅✅ 强烈推荐 | 后量子加密默认启用,零代码改动 |
| AI/数据密集型应用 | ✅✅ 推荐 | 惰性常量+Vector API,但预览特性需评估 |
| 需要长期稳定支持的生产环境 | ⚠️ 需评估 | JDK 25 LTS更稳妥 |
| 依赖Graal JIT的项目 | ❌ 不推荐 | JVMCI被移除,需等待GraalVM跟进 |
更多项目实战在Java突击队网:susan.net.cn/project
十二、写在最后
回到最初的问题:JDK 27值不值得升级?
我的判断是:值得尝鲜,但生产环境等JDK 28或JDK 29 LTS更稳妥。
JDK 27最大的价值不在于某个“炫酷的新语法”,而在于三个默认行为变更:
G1成为全场景默认——你什么都不做,启动更快、延迟更低。
对象头砍到64位——你什么都不做,堆占用降25%。
后量子加密默认启用——你什么都不做,HTTPS通信就获得了抗量子保护。
这三件事加起来,是实打实的运行时收益,不是“预览版的预览版”。
预览特性里,结构化并发和惰性常量是最值得关注的。
结构化并发如果转正,会改变Java并发编程的写法;惰性常量如果转正,会成为AI应用的标配。
JDK 27不是LTS,如果你现在的生产环境跑在JDK 21或JDK 25 LTS上,不用急着升。
但如果你在开发新项目、跑实验环境、或者想提前感受Java的演进方向——JDK 27值得下载跑一跑。