Java 25 上 LTS:这笔账怎么算

2026-09-24 Aryee 1

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 那条线时要重新核一遍参数是不是还合理。

一个务实的判断标准

值不值得升,我一般只看两条:

  1. 你现在这档的公开安全更新还剩多久。 到期时间就是硬约束,其他都是可选项。
  2. 有没有一个具体指标能证明新版本更好。 堆占用、P99 停顿、启动时间,任选一个能落到你系统痛点上的,量出来对比。

两条都不满足就先不动。升级不是 KPI,把 25 的新语言特性写进代码,等于主动放弃了向下降级的能力——这才是最实在的成本。

你手上那套系统,卡在哪一步?

留个手机号和方便的时间,我打过来先听你说现状与约束,再给可执行的判断:这套系统是该继续修、该动哪里,还是干脆重做一版设计更省。

约一次沟通不接纯 UI 外包,只做系统层面的问题。