选 Java 版本这件事,看起来简单------去官网看一眼最新 LTS,下载,部署,完事。但真正在生产环境踩过坑的团队都知道,版本号只是冰山一角。LTS 到底保了什么、Oracle 的免费协议什么时候到期、JDK 发行版选哪家、升级时哪些 API 会突然消失------这些问题每一个都可能在某个周五晚上让整个团队加班到凌晨。
这篇文章不打算罗列 Java 的 changelog,而是把生产环境中真正会遇到的版本决策问题拆开来看:LTS 的含义、Oracle 许可的连环变化、发行版的选择策略、升级时的典型陷阱,以及一套可以在团队内部落地的选型框架。
LTS 到底是什么
LTS 是 Long-Term Support 的缩写,翻译过来叫"长期支持版本"。这个概念不是 Java 从第一天就有的------在 Java 8 之前,Java 的发布节奏完全取决于 Oracle 的心情,版本号从 1.0 蹦到 1.7,中间还穿插了 Java 5、Java 6 这样的命名跳跃。没有固定节奏,没有可预测的升级窗口,企业只能等 Oracle 发话。
2017 年 9 月,Java 9 发布,同时带来了一个重要的架构变化:Java 平台模块系统(JPMS)。也是从这个版本开始,Oracle 决定把发布周期改成每六个月一次------每年 3 月和 9 月各发一个大版本。这个节奏的好处是明显的:新特性可以更快到达开发者手中,不用等三年才看到下一个大版本。但问题也来了:企业不可能每六个月就升级一次生产环境的 Java。
于是 LTS 应运而生。Oracle 的设计是:每六个月发一个大版本,其中每隔几年挑一个作为 LTS 版本,给它更长的支持周期。非 LTS 版本只有六个月的寿命,过了就不再有安全更新。LTS 版本则能获得多年的安全补丁和 bug 修复。
具体的 LTS 节奏经历了两次调整:
第一阶段是 Java 8 到 Java 11。LTS 间隔三年,Java 8(2014 年 3 月)是第一个被官方明确为 LTS 的版本,Java 11(2018 年 9 月)是第二个。三年等一次升级窗口,对企业来说还算从容。
第二阶段从 Java 17 开始。Oracle 把 LTS 间隔从三年缩短到两年。Java 17(2021 年 9 月)、Java 21(2023 年 9 月)、Java 25(2025 年 9 月)都是按两年节奏来的 LTS。下一个 LTS 是 Java 29,预计 2027 年 9 月发布。缩短间隔的原因是 Java 语言演进速度加快------Record、Pattern Matching、Virtual Thread 这些特性需要更快进入稳定版本。
目前的 LTS 版本一览:
| LTS 版本 | 发布时间 | 类型 | Oracle Premier Support | Oracle Extended Support |
|---|---|---|---|---|
| Java 8 | 2014-03 | LTS | 2022-03 结束 | 2030-12 结束 |
| Java 11 | 2018-09 | LTS | 2023-09 结束 | 2032-01 结束 |
| Java 17 | 2021-09 | LTS | 2026-09 结束 | 2029-09 结束 |
| Java 21 | 2023-09 | LTS | 2028-09 结束 | 2031-09 结束 |
| Java 25 | 2025-09 | LTS | 2030-09 结束 | 2033-09 结束 |
这张表里有几个关键信息需要拆开看。
"Premier Support"是 Oracle 的标准支持周期,LTS 版本通常获得 5 年 Premier Support。"Extended Support"是延长支持,再加 3 年。加起来一个 LTS 版本最多有 8 年的 Oracle 官方支持周期。但"Oracle 官方支持"和"免费使用"是两回事------后面会详细讲许可的问题。
非 LTS 版本(比如 Java 22、23、24、26)不在上表中,因为它们只有六个月的生命周期。六个月后不再有任何安全更新。New Relic 2024 年的 Java 生态报告显示,非 LTS 版本在生产环境的采用率一直低于 3%------绝大多数企业只用 LTS。
这也是为什么 LTS 对生产环境如此重要:它不只意味着"长期支持",更意味着一个可预测的升级窗口。团队可以围绕两年一次的 LTS 周期来规划技术债清理和框架升级,而不是被六个月一次的非 LTS 版本牵着走。
Oracle JDK 的许可连环雷
如果说 LTS 是技术层面的"能不能长期用"的问题,那 JDK 的许可是法律层面的"能不能合法用"的问题。Oracle 在这件事上已经炸了好几次雷。
2019 年 4 月之前,Oracle JDK 在 Binary Code License(BCL)协议下免费提供商用。大部分企业用 Oracle JDK 跑生产,不花一分钱。2019 年 4 月,Oracle 宣布 Java 8 及以后版本改用 Oracle Technology Network(OTN)协议------商用必须付费购买 Java SE Subscription。这个变化影响极其广泛,因为当时绝大多数企业生产环境跑的是 Oracle JDK 8。一夜之间,继续用 Oracle JDK 8 接收安全补丁变成了一笔每年每员工上百美元的订阅费。
2021 年 9 月,Java 17 发布时,Oracle 又改了规则------这次往好的方向改。新引入了 No-Fee Terms and Conditions(NFTC)协议,允许免费商用。Oracle JDK 17 及后续版本在 NFTC 下免费提供。
但 NFTC 有一个容易被忽略的陷阱:免费期限。
NFTC 不是永久免费的。对于 LTS 版本,Oracle 的规则是:NFTC 覆盖该 LTS 发布后的第一个一年。下一个 LTS 发布后的一年,上一个 LTS 的 NFTC 就到期了。具体来说:
| Oracle JDK 版本 | NFTC 免费截止 | 到期后许可 |
|---|---|---|
| Java 17 | 2024-09 | 后续更新需 Java SE Subscription |
| Java 21 | 2026-09 | 后续更新需 Java SE Subscription |
| Java 25 | 2028-09 | 后续更新需 Java SE Subscription |
Java 17 的 NFTC 在 2024 年 9 月到期。这意味着从 2024 年 10 月起,Oracle JDK 17 的后续安全更新需要付费订阅。很多团队到 2025 年才发现自己已经在不合规的状态下运行了好几个月------Oracle 并不会主动通知,它只是在许可页面的某个角落更新了条款。
Java 21 的 NFTC 在 2026 年 9 月到期。如果团队现在用 Oracle JDK 21 跑生产,需要在这个日期之前决定:要么升级到 Java 25(NFTC 覆盖到 2028 年 9 月),要么购买 Java SE Subscription,要么换发行版。
对于非 LTS 版本(如 Java 24、26),NFTC 覆盖其完整的六个月生命周期------没有到期问题,但也没有长期使用价值。
还有一个细节:2025 年 9 月 Java 25 发布时,Oracle 宣布 Java 25 的 NFTC 面向"All Users"------不仅限商用,个人开发也包含。但这个"NFTC 直到 2028 年 9 月"的规则没变。
换句话说,Oracle JDK 的许可模式变成了:每个 LTS 免费 3 年(从发布到下一个 LTS 发布后一年),之后必须付费才能继续接收安全更新。三年看起来不短,但如果一个团队从 Java 17(2021 年发布)开始用,到 2024 年 9 月就得做决定------要么花钱、要么换发行版、要么升级到 Java 21。而 Java 21 的免费窗口也只到 2026 年 9 月。
这就是为什么"选 Oracle JDK 还是选 OpenJDK 发行版"在 2025 年成了一个生产环境的必答题。
OpenJDK 发行版:不止 Oracle 一家
Oracle JDK 只是 OpenJDK 的一个发行版------就像 RHEL 只是 Linux 内核的一个发行版一样。OpenJDK 是源代码项目,任何人都可以基于它编译构建自己的 JDK。Oracle JDK 在此基础上加了一些自己的东西(比如 Java Flight Recorder 曾经是 Oracle 专属的,后来开源了),但从 Java 11 开始,Oracle JDK 和 OpenJDK 在功能上几乎没有差异。
目前主流的 OpenJDK 发行版有这些:
| 发行版 | 维护方 | 特点 | 支持策略 |
|---|---|---|---|
| Oracle JDK | Oracle | 官方发行版,NFTC 有到期限制 | LTS 免费 3 年,之后需订阅 |
| Oracle OpenJDK | Oracle | 纯社区构建,非 LTS only | 非 LTS 全生命周期免费 |
| Eclipse Temurin | Adoptium 工作组 | 社区驱动,TCK 认证,最广泛使用的免费发行版 | LTS 8 年免费支持 |
| Amazon Corretto | AWS | AWS 内部使用并开源,AWS 回灌上游 | LTS 免费支持到官方 EOL |
| Azul Zulu | Azul Systems | 商业公司提供免费版和付费增强版 | LTS 8 年免费 + 2 年延长 |
| Red Hat OpenJDK | Red Hat | RHEL 默认 JDK,OpenJDK 项目主要贡献者 | LTS 配合 RHEL 支持周期 |
| BellSoft Liberica | BellSoft | 全平台支持,包括 Alpine Linux musl | LTS 免费支持 |
| IBM Semeru | IBM | OpenJ9 虚拟机替代 HotSpot,内存占用低 | LTS 免费支持 |
| SapMachine | SAP | SAP 内部使用并开源 | LTS 免费支持 |
这张表的关键不是"哪个最好",而是"哪个最适合我们的场景"。几个判断维度:
运行环境如果在 AWS 上,Amazon Corretto 是最自然的选择------AWS 自己在用,性能针对 EC2 做过优化,并且它回灌到 OpenJDK 上游的补丁最终惠及所有人。Corretto 对 Java 8 的支持延续到 2030 年 12 月,和 Oracle 的 Extended Support 一致,但完全免费。
Eclipse Temurin 是 Adoptium 工作组维护的构建版本。Adoptium 的前身是 AdoptOpenJDK,2021 年迁移到 Eclipse 基金会后更名。Temurin 是目前社区采用率最高的免费发行版------New Relic 2024 报告显示,Eclipse Adoptium(Temurin)已经超越 Oracle 成为生产环境最常用的 JDK 来源。所有 LTS 版本都有 8 年免费支持,TCK 认证保证兼容性。
如果团队在用 RHEL 或 CentOS Stream,Red Hat OpenJDK 是默认选择。Red Hat 是 OpenJDK 项目最大的非 Oracle 贡献者------Java 11 和 Java 17 的很多关键修复都来自 Red Hat 团队。Red Hat 对 Java 8 的支持到 2030 年,对 Java 11 的支持到 2026 年 10 月。
Azul Zulu 适合需要商业支持但又不想用 Oracle 的团队。Azul 提供 Zulu(免费)和 Zing / Prime(付费,带 C4 垃圾回收器)两条产品线。Zulu 的免费版对所有 LTS 提供 8 年支持,和 Temurin 持平。
IBM Semeru 比较特殊------它用 Eclipse OpenJ9 虚拟机替代了标准的 HotSpot。OpenJ9 的内存占用比 HotSpot 低 30-60%,在容器化环境(尤其是内存受限的微服务)中优势明显。代价是峰值吞吐量略低于 HotSpot,且某些 JVM 工具的兼容性需要验证。
选发行版的核心原则:先确认是否需要 Oracle JDK 的专属特性(基本上从 Java 11 开始已经没有了),不需要就直接选 OpenJDK 发行版。按云厂商和操作系统对齐------AWS 上用 Corretto,RHEL 上用 Red Hat OpenJDK,其他场景用 Temurin。需要商业 SLA 的选 Azul 或 Red Hat 的付费版。
生产环境怎么选版本
搞清楚了 LTS 的含义和发行版的差异,接下来的问题是:具体选哪个版本?这个问题没有标准答案,但有一套可以复用的判断框架。
生产环境选 Java 版本的决策维度可以归纳成五条:
第一,只选 LTS 版本。非 LTS 版本六个月就 EOL,没有安全更新。生产环境跑非 LTS 等于裸奔------出了安全漏洞没有补丁可打。2024 年 New Relic 的数据显示,非 LTS 版本在生产环境的占比不到 3%,这不是巧合。
第二,看支持周期是否覆盖项目的预期寿命。如果项目计划运行 5 年,当前 Java 17 的 Oracle Extended Support 到 2029 年 9 月------看起来够了,但 Oracle JDK 17 的免费 NFTC 已经过期。如果用 Temurin 或 Corretto,Java 17 免费支持到 2029 年(Temurin)或 2029 年(Corretto),这是 OK 的。但如果用 Oracle JDK 17 且没有订阅,已经在不合规状态。
第三,看依赖库和框架的兼容性。Spring Framework 6 要求 Java 17+,Spring Boot 3 同样。如果项目还在用 Spring Boot 2.x,最高只能用 Java 17(Spring Boot 2.x 官方测试到 Java 17)。强行在 Java 21 上跑 Spring Boot 2.x 不是不行,但没有官方支持,出了问题只能自己查。
第四,看 GC 和性能需求。Java 21 的 Virtual Thread(虚拟线程)对高并发 I/O 密集型应用是质变级提升------一个虚拟线程的成本约 2KB,而平台线程约 1MB。如果项目是 I/O 密集型的微服务,Java 21 的虚拟线程可能直接砍掉一半的服务器。但如果项目是 CPU 密集型的,虚拟线程帮助不大,GC 策略的选择更重要------ZGC 在 Java 21 已经稳定,适合对暂停时间敏感的场景。
第五,看团队的学习成本和迁移风险。从 Java 8 直接跳到 Java 21 跨度太大------模块系统、sealed class、record、pattern matching、switch expression、text block、virtual thread------每一个都是新的语法或概念。分两步走(8→17,17→21)比一步到位更安全,每一步都有充足的回退窗口。
基于这些维度,目前(2025-2026 年)的场景推荐:
| 场景 | 推荐版本 | 理由 |
|---|---|---|
| 新项目,无历史包袱 | Java 25 | 最新 LTS,8 年支持窗口,语言特性最完整 |
| 近两年升级过的项目 | Java 21 | 已经稳定 2 年,生态完全适配,虚拟线程可用 |
| 从 Java 8/11 迁移 | Java 17(第一步)→ Java 21(第二步) | Java 17 是模块系统后的稳定基线,Spring Boot 3 最低要求 |
| 对暂停时间极敏感 | Java 21+,启用 ZGC | ZGC 在 Java 21 标记为 stable,暂停时间 <1ms |
| 内存受限的容器环境 | Java 21+,使用 IBM Semeru OpenJ9 | OpenJ9 内存占用比 HotSpot 低 30-60% |
| 仍在跑 Java 8 的遗留系统 | 尽快迁移到 Java 17 或 21 | Oracle JDK 8 免费补丁早已停止,多发行版支持到 2030 年 |
这张表的核心逻辑是:新项目追最新 LTS,老项目分步迁移不跳级,特殊场景按需选 GC 和发行版。
生产环境升级的典型坑
版本选好了不代表万事大吉------升级过程本身才是真正的考验。Java 从 8 到 25 经历了三次大的破坏性变化:模块系统引入、强封装默认开启、API 大规模清理。每一个都可能在生产环境引发事故。
模块系统与反射访问
Java 9 引入了 Java 平台模块系统(JPMS),把 JDK 内部拆成了约 70 个模块。这对反射访问的影响是毁灭性的------大量依赖 sun.misc.Unsafe 或通过反射访问 JDK 内部 API 的库直接报错。
Java 9 到 Java 15 期间,模块系统对反射访问采取的是"警告但不阻止"策略:运行时会打印 WARNING: An illegal reflective access operation has occurred,但操作仍然成功。可以通过 --illegal-access=permit(Java 9-15 默认)控制行为。
Java 16 开始,强封装(Strong Encapsulation)默认开启。--illegal-access 选项被移除,反射访问 JDK 内部 API 直接抛出 InaccessibleObjectException。这意味着之前只是打印警告的代码,从 Java 16 起直接不能运行。
解决方案是使用 --add-opens JVM 参数手动打开需要的模块:
csharp
java --add-opens java.base/java.lang=ALL-UNNAMED \
--add-opens java.base/java.util=ALL-UNNAMED \
--add-opens java.base/java.lang.reflect=ALL-UNNAMED \
-jar app.jar
每一行 --add-opens 打开一个模块的某个包给未命名模块(即 classpath 上的代码)。问题是:怎么知道需要打开哪些模块?
最实际的做法是先在测试环境跑一遍,看 InaccessibleObjectException 报什么就加什么。Lombok、Jackson、Guava、Mockito 等常用库在较新版本中已经不再依赖反射访问 JDK 内部 API,但旧版本仍然需要 --add-opens。
如果项目还在用 Java 8,升级到 Java 17+ 时,反射访问是最容易翻车的点。建议先用 jdeps 工具扫描代码依赖:
css
jdeps --jdk-internals --multi-release 17 my-app.jar
jdeps 会列出所有依赖 JDK 内部 API 的地方,以及建议的替代方案。在升级前先跑一遍 jdeps,可以提前发现大部分反射访问问题。
被移除的 API
Java 升级的一大风险是 API 被移除。Oracle 的策略是:先 @Deprecated(标记弃用),再 @Deprecated(forRemoval=true)(标记即将移除),最后在某个大版本真正删除。但很多团队不看 release notes,也不跑 deprecation 扫描。
Java 11 移除了一大批 API,其中影响最大的是 javax.xml.bind(JAXB)、javax.xml.ws(JAX-WS)、javax.annotation(@Generated 等)、javax.transaction(JTA)、javax.activation(JAF)。这些在 Java 9-10 被标记弃用并从 JDK 中移除,Java 11 彻底删掉。如果代码直接 import 这些包,升级到 Java 11 会直接编译失败。
解决方案是引入第三方依赖替代------比如用 jakarta.xml.bind 替代 javax.xml.bind。Jakarta EE 是 Java EE 迁移到 Eclipse 基金会后的新命名空间,包名从 javax.* 改为 jakarta.*。
Java 17 移除了 Security Manager。System.setSecurityManager() 在 Java 17 标记弃用,Java 18 进一步限制,最终在后续版本移除。如果项目用 Security Manager 做权限控制(已经很少见了),需要迁移到其他方案。
Java 18 开始,Object.finalize() 被标记为 @Deprecated(forRemoval=true)。finalization 机制(GC 时调用 finalize() 方法)被认为设计有缺陷------不可靠、性能差、容易导致内存泄漏。Java 21 继续强化这个弃用,替代方案是 java.lang.ref.Cleaner。在 Java 25 中,finalization 已经默认禁用,需要通过 --finalization=enabled 才能恢复。
Java 21 引入了密封类(Sealed Class)、记录类(Record)、模式匹配等特性,同时移除了 RMI Activation、去掉了一些过时的 GC 策略(如 Serial GC 的某些旧参数不再有效)。
这些 API 变化的共同特点是:不会在升级时自动报错。@Deprecated 只在编译时产生 warning,如果团队没有配置 -Werror(把 warning 当 error 处理),这些 warning 会被忽略。直到 API 真正被移除,运行时才会抛 NoSuchMethodError 或 ClassNotFoundException------而这个时候已经部署到生产环境了。
GC 策略的变化
垃圾回收器的选择在 Java 升级时经常被忽略,但它对生产环境的延迟和吞吐量有直接影响。
Java 8 的默认 GC 是 Parallel GC(也叫 Throughput Collector),追求吞吐量但暂停时间可能达到数百毫秒。Java 9 开始默认改为 G1 GC,目标是将暂停时间控制在 200ms 以内。如果团队从 Java 8 升级到 Java 17+,且没有手动指定 GC,默认行为已经变了------G1 对内存的分配方式和 Parallel 不同,可能导致应用内存占用增加 10-20%。
ZGC(Z Garbage Collector)在 Java 15 标记为生产可用,在 Java 21 进一步稳定。ZGC 的暂停时间可以做到 <1ms,不随堆大小增长,适合大内存(几十 GB 到 TB 级别)且对延迟敏感的应用。但 ZGC 的吞吐量比 G1 略低,且在 Java 21 之前需要手动开启(-XX:+UseZGC)。
Shenandoah GC 由 Red Hat 开发,在 Java 15 加入主线。它的设计目标和 ZGC 类似(低暂停时间),但实现方式不同。Shenandoah 在 Red Hat OpenJDK 和 Temurin 中可用,但 Oracle JDK 不包含 Shenandoah------这是 Oracle JDK 和 OpenJDK 发行版之间的一个功能差异。
升级时的典型 GC 坑:
从 Java 8 升级到 Java 17+,默认 GC 从 Parallel 变成 G1。如果应用对吞吐量敏感(比如批处理任务),可能需要手动切回 Parallel GC:-XX:+UseParallelGC。如果对延迟敏感,G1 或 ZGC 是更好的选择,但需要重新调优 GC 参数------Java 8 时代的 G1 参数在 Java 17 上可能已经不适用。
另一个坑是 JVM 参数的变化。Java 9-17 期间,大量旧参数被移除或改名。比如 -XX:UseConcMarkSweepGC(CMS GC)在 Java 9 被标记弃用,Java 14 彻底移除。如果启动脚本里还有这个参数,Java 14+ 会直接拒绝启动。类似地,-XX:+UseBiasedLocking(偏向锁)在 Java 15 被弃用并禁用。
建议升级时先用 java -XX:+PrintFlagsFinal -version 对比新旧版本的 JVM 参数列表,确认所有启动参数在新版本中仍然有效。
第三方依赖的兼容性
Java 版本升级最大的风险不在 JDK 本身,而在第三方依赖。一个典型案例:Lombok。
Lombok 通过编译期注入修改 AST 来实现 @Data、@Builder 等注解。这个机制深度依赖 JDK 内部 API(javac 的 ProcessingEnvironment 内部实现)。几乎每次 Java 大版本升级,Lombok 都需要发一个新版本来适配。Java 16 发布时,Lombok 用不了,社区等了两个月才修复。如果项目重度依赖 Lombok 且没有锁版本,升级 Java 版本可能导致整个项目编译失败。
类似的情况还有:
| 依赖库 | 兼容性问题 | 建议 |
|---|---|---|
| Lombok | 依赖 javac 内部 API,每个 Java 版本需适配 | 升级 Java 前先确认 Lombok 版本兼容性 |
| Mockito | 依赖反射访问 JDK 内部类 | 使用 5.x+ 版本,支持 Java 17+ |
| ByteBuddy | 依赖字节码注入,需适配新版本 class file format | 跟随 Java 版本升级 |
| cglib | 已停止维护,不兼容 Java 16+ | 迁移到 ByteBuddy |
| JaCoCo | 需适配新版本 class file format | 使用最新版 |
| AspectJ | 依赖编译器内部 API | 使用 1.9.x+ 版本 |
升级 Java 版本前,必须对项目所有直接和间接依赖做一次兼容性扫描。Maven 用户可以用 mvn dependency:tree 拉出完整依赖树,然后逐个检查是否有已知兼容性问题。Gradle 用户可以用 ./gradle dependencies。
更稳妥的做法是:先在 CI 环境用目标 Java 版本编译,看有没有编译错误或 deprecation warning。然后在测试环境用目标 Java 版本跑完整测试套件。最后在预发布环境灰度运行至少一周,观察 GC 日志、内存使用、响应延迟有没有异常。
决策框架
把上面所有因素整合起来,生产环境选 Java 版本的决策流程可以概括成几步:
第一步:确定当前状态。现在用的什么版本、什么发行版、什么 GC、有哪些重依赖。这是所有后续决策的基础。
第二步:确定升级窗口。当前版本的支持周期还剩多久?如果还剩 2 年以上且无安全风险,可以不急着升。如果不到 1 年,需要立即开始规划。Oracle JDK 17 的 NFTC 已经过期(2024 年 9 月),如果还在用 Oracle JDK 17 且没有订阅,这已经是一个需要立即处理的问题。
第三步:选目标版本。只考虑 LTS。如果当前是 Java 8 或 11,目标至少是 Java 17。如果当前是 Java 17,目标是 Java 21 或 25。不建议跨超过两个 LTS 升级------8 到 17 已经是大跨度,8 到 21 风险太高。
第四步:选发行版。如果当前用 Oracle JDK,评估是否真的需要------从 Java 11 开始 Oracle JDK 和 OpenJDK 在功能上没有差异。切换到 Temurin 或 Corretto 不需要改代码,只需要换 Docker 基础镜像或安装包。
第五步:跑兼容性扫描。用 jdeps 扫描 JDK 内部 API 依赖,用 mvn dependency:tree 或 ./gradle dependencies 拉出依赖树,逐个检查第三方库的兼容性矩阵。特别关注 Lombok、Mockito、字节码操作库。
第六步:在 CI 环境用目标版本编译和测试。配置 -Werror 让 deprecation warning 变成编译错误,提前发现被移除的 API。
第七步:在预发布环境灰度。观察 GC 日志(对比升级前后的暂停时间、GC 频率、内存占用)、CPU 使用率、响应延迟。如果用 G1,确认暂停时间是否在可接受范围内。如果切 ZGC,确认吞吐量是否满足 SLA。
第八步:制定回退方案。升级失败时能否快速回退到旧版本?这要求新旧版本能在部署层面无缝切换------容器化部署用镜像 tag 回滚,传统部署保留旧版本安装包。
这套流程看起来繁琐,但每一步都对应着真实生产事故的教训。Java 版本升级不是"换个 JDK 装一下"的事------它涉及编译器、运行时、GC、依赖库、JVM 参数等多个层面的变化。跳过任何一步验证,都可能在生产环境踩坑。
写在最后
Java 的 LTS 机制给了企业一个可预期的升级节奏------每两年一个 LTS,每个 LTS 八年支持周期。但"可预期"不等于"无脑选"。Oracle 的 NFTC 许可有到期陷阱,OpenJDK 发行版有各自的定位和优势,升级过程有模块系统、API 移除、GC 变化、依赖兼容性等多重风险。
生产环境选 Java 版本的核心原则可以浓缩成一句话:选 LTS,选对发行版,提前扫描依赖,灰度验证,永远有回退方案。
这句话说起来容易,执行起来每一步都有细节。但这些细节恰恰是区分"从容升级"和"凌晨三点还在排查生产事故"的分界线。