必须先做兼容性检查,否则直接换JDK17易致启动失败、序列化崩溃、反射报错或线上500;需用jdeps扫描sun.*等内部API依赖,核查druid≥1.2.16、fastjson2、zookeeper≥3.5.x、log4j2≥2.17.0、Spring Boot≥2.7.x、lombok≥1.18.30,并替换javax→jakarta包,修复模块访问限制及GC/ClassLoader等运行时差异。

必须先做兼容性检查,否则直接换 JDK 17 很可能启动失败、序列化崩溃、反射报错,甚至线上请求 500。
用 jdeps 扫描内部 API 依赖
很多老项目偷偷用了 sun.misc.BASE64Encoder、sun.nio.ch.DirectBuffer 这类内部类,JDK 9+ 已强封装,JDK 17 默认拒绝访问。不提前发现,运行时直接抛 InaccessibleObjectException 或 NoClassDefFoundError。
- 执行命令:
jdeps --jdk-internals your-app.jar(或对target/classes目录扫描) - 重点看输出里带
sun.*、com.sun.*、jdk.internal.*的行,每一条都得人工确认是否可替换 - 若项目含 JNI 或字节码增强(如
ByteBuddy、javassist),还要加--multi-release 17参数验证多版本 class 兼容性
跑 mvn dependency:tree 核查关键依赖版本
不是所有“支持 Java 17”的库都真能用——有些只在测试环境跑通,一到生产就因 GC 行为变化或模块访问限制崩掉。
- 重点关注:
druid(必须 ≥ 1.2.16)、fastjson(1.x 不行,得切fastjson2)、zookeeper(≤ 3.4.x 直接拒启)、log4j(1.x 已被移除,2.x 需 ≥ 2.17.0)、mybatis(≥ 3.5.10) - Spring Boot 项目必须核对
spring-boot-starter-web等 starter 版本:2.7.x 是最低支持 JDK 17 的稳定版,低于它会因javax.servlet → jakarta.servlet迁移失败而启动报错 - 若用
lombok,确认 ≥ 1.18.30;mockito≥ 4.0.0,否则反射 mock 会失败
检查 javax.* 包迁移和模块访问异常
JDK 17 默认启用 --illegal-access=deny,任何未经许可的跨模块反射都会被拦住,典型表现是 Spring 启动卡在 AbstractAutowireCapableBeanFactory、Hibernate 报 java.lang.reflect.InaccessibleObjectException。
- 启动时加临时 JVM 参数快速验证:
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED(按实际报错类补全) - 但别留着上线——这等于开后门。真正要改的是代码或依赖:把
javax.validation换成jakarta.validation,javax.xml.bind补jakarta.xml.bind-api+jaxb-runtime - 如果有自定义
SecurityManager或调用Unsafe,必须迁移到VarHandle或MethodHandles.Lookup
验证构建与运行时行为差异
JDK 17 默认 GC 已从 Parallel 切到 G1,ZGC/Shenandoah 需显式开启;同时 String 内部结构、HashMap 扩容逻辑、ClassLoader 加载顺序都有微调,某些靠“巧合”工作的代码会暴露问题。
- 本地构建用
maven-compiler-plugin显式设<release>17</release>,避免编译出仅在高版本 JVM 能跑的字节码 - CI 流水线中,用
java -XX:+PrintGCDetails对比 GC 日志,确认无频繁 full gc 或晋升失败 - 跑一遍真实业务链路(不只是单元测试):比如文件上传、JSON 序列化、定时任务触发、数据库连接池获取,这些最容易暴露 JAXB 移除、JDBC 驱动不兼容、线程上下文类加载器失效等问题
最常被跳过的一步是:没在和生产一致的 JVM 参数下压测。比如开了 -XX:+UseZGC 却没配 -XX:SoftMaxHeapSize,或漏了 --enable-preview(如果用了虚拟线程),结果灰度时 CPU 突增 300% 才发现。兼容性检查不是 checklist 走完就结束,而是要把“能启动”和“能稳跑”分开验证。


















