Java 25 上 LTS:这笔账怎么算
JDK 25 的 GA 日期是 2025 年 9 月 16 日,按 Oracle 的节奏这是当前这一档 LTS。关于它的新功能介绍已经很多,这篇只谈升级决策本身——因为多数团队卡住的不是「有什么新的」,而是「我这堆代码能不能换过去,换了值不值」。
先看会影响升级过程的几条
这几条不是加分项,是必须提前处理的:
JEP 503:移除 32 位 x86 端口。 还在发 32 位安装包、或者生产机上跑着 32 位 JVM 的,这条路直接断了。规模不大的团队通常在这一步才发现自己有一台忘了的旧机器。
finalize() 还没删,但已经到门口了。 Object.finalize() 在 Java SE 25 里仍是 @Deprecated(forRemoval=true)——注意是「标记待移除」而不是「已移除」,所以现在编译不会报错。真正的影响在别处:它一直是资源释放的错误答案,靠 GC 触发既不及时也不保证执行。还依赖它兜底的封装库,这一轮排查完就该改掉,别等它真被删掉那次再改。
安全管理器这条路已经彻底封死。 从 JDK 24 起 Security Manager 是永久禁用状态:命令行带 -Djava.security.manager 会让 JVM 直接启动失败,System.setSecurityManager() 抛 UnsupportedOperationException,java.policy 文件机制也一并移除(相关 API 自 JDK 17 的 JEP 411 起弃用,接口本身尚未删)。启动脚本里还留着这类参数的,会在换 JDK 的第一天就以最直白的方式暴露——这反而是好事。
JEP 514 / 515:AOT 相关的两条。 一条是命令行参数整理,一条是 AOT 方法画像。用得上的是启动时间敏感的短生命周期进程——Serverless、CLI 工具、以及那些每天重启很多次的 Pod。传统常驻服务收益有限,不必为了它改部署方式。
收益主要落在哪
按对现有系统的影响大小排,我认为是这样:
| JEP | 内容 | 谁会真的受益 |
|---|---|---|
| 519 | 紧凑对象头 | 堆里小对象密集的内存占用下降 |
| 521 | 分代 Shenandoah | 要低停顿又不想换 G1 的团队 |
| 510 | 密钥派生函数 API | 自己实现过 PBKDF2 封装的代码可以删掉了 |
| 511 | 模块导入声明 | 单文件脚本与教学代码,样板减少 |
| 512 | 紧凑源文件 | 同上,小工具不必再写 main 的架子 |
| 513 | 灵活的构造器体 | 校验前置的写法终于不用绕了 |
前两条要单独提醒一句,因为它们是最容易被写成「默认就更快/更省」的:
- 519 紧凑对象头做成了产品特性,但按 JEP 页面自己的说明,它没有改变默认的对象头布局。也就是说装上新 JDK 不等于自动拿到那部分内存收益,要不要开、开了之后堆行为和 GC 统计怎么变,得自己按参数验一轮。
- 521 分代 Shenandoah 同理:非分代模式并没有被移除,仍是默认。想用分代模式要显式选。
另外两条是纯开发者体验收益,不是运行时收益:511 和 512 别写进升级收益报告去说服业务方。真正能报数字的是 519(堆占用)和 GC 相关的几条(停顿),而它们的效果强依赖你的对象分布和参数选择。
所以顺序应该是反过来的:先在预发环境用真实流量跑一轮,把堆占用和 P99 停顿量出来,再决定要不要为了这两个数字动生产。我见过太多团队先升了再找收益,最后写出来的理由是「更安全」——那也成立,但要提前说清楚,别假装是性能驱动。
有几条别指望
写 25 的介绍时最容易把这几条当成「已经落地」,实际都还是预览:
- JEP 505 结构化并发:在 25 里是第五次预览,到 26(JEP 525)是第六次。没定。
- JEP 507 模式匹配中的原始类型:25 第三次预览,26(JEP 530)第四次。
- JEP 508 / 529 Vector API:25 第十次孵化,26 第十一次。
- JEP 502 Stable Values:25 仍是预览。
区分它们不是学究气。预览特性要用 --enable-preview 编译,而开启了 preview 的 class 文件不能在不带该选项的 JVM 上运行,也锁死了 JDK 大版本(预览 API 在不同版本间不保证兼容)。也就是说,为了用结构化并发而把生产服务编成 preview 模式,等于主动放弃了后续版本的升级通道——这跟升 LTS 的目的正好相反。
语言特性想早用,走 Kotlin 或者用库;升 LTS 的目标是稳,不是新。
三条路径分别怎么排
从 Java 8 出发。 这一档最麻烦,中间隔着 JDK 9 的模块系统,以及 11 / 17 两档 LTS 各自的不兼容点。别指望一次到位,先升 17 稳住,把 javax → jakarta 这类依赖换代单独做成一个里程碑,之后再上 25。跳过中间档的升级,出问题时分不清是哪一层引入的。
从 Java 11 / 17 出发。 主要是构建链的问题:编译插件、静态检查工具、字节码增强类库(Mock 框架、APM 探针、老版本的 ASM/CGLIB)能不能认 25 的字节码。这一步在本地就能测完:换 JDK 后先跑 mvn verify,工具链不兼容是这一档升级最常见的失败原因,而不是业务代码。
从 Java 21 出发。 21 到 25 是最平的一档,语言层面几乎不会挡路。重点放在两处:GC 默认策略的变化是否影响你的容量评估,以及依赖树里有没有靠反射访问 JDK 内部实现的库——这类库在新版本上会报 InaccessibleObjectException,通常藏在序列化组件和监控埋点里。
25 之后那两档已经发布了,它们决定你要不要停在这一档
如果升 25 是为了「接下来几年不用再动」,那有两件事需要一起知道——这两档都已经在眼前,不是将来的事:
- JDK 26(2026 年 3 月发布,非 LTS 档):有一条对现网更实用的改动是给 HTTP Client API 加上 HTTP/3;另有一项让 AOT 对象缓存不再绑定特定 GC,和一项通过减少同步来提升 G1 吞吐。还有一处容易挡路:Applet API 是在这一档才被真正移除的——如果你手上还有靠它编译通过的老代码,卡住你的是 26 不是 25。
- JDK 27(2026 年 9 月 15 日发布):JEP 523 把 G1 设为所有环境的默认垃圾收集器。这一档仍是非 LTS,所以下一档 LTS 之前你有时间观察,但这条变化的影响面是运行时行为,不是 API。
所以我现在的建议是:25 是当前该停的那一档(LTS、生态支持齐),但排期时要把 G1 成为默认这件事提前记着——尤其是那些因为历史原因显式指定了别的 GC 的服务,升到 27 那条线时要重新核一遍参数是不是还合理。
一个务实的判断标准
值不值得升,我一般只看两条:
- 你现在这档的公开安全更新还剩多久。 到期时间就是硬约束,其他都是可选项。
- 有没有一个具体指标能证明新版本更好。 堆占用、P99 停顿、启动时间,任选一个能落到你系统痛点上的,量出来对比。
两条都不满足就先不动。升级不是 KPI,把 25 的新语言特性写进代码,等于主动放弃了向下降级的能力——这才是最实在的成本。
本文作者:Aryee 发布时间:2026-09-24 20:28
本文出处:ARYEE.cn 固定链接:https://www.aryee.cn/archives/java-25-lts-upgrade.html
转载或引用请连这一行一起带走;文中的代码与结论按当时环境成立,组件升级后请以官方文档为准。