ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从Oracle JDK 8迁移至OpenJDK 17:实战指南与避坑全记录

2026/8/15 4:09:08 拓冰建站 浏览量
从Oracle JDK 8迁移至OpenJDK 17:实战指南与避坑全记录 1. 项目概述从Oracle JDK到OpenJDK的迁移决策最近在为一个老项目的维护和迁移做准备遇到了一个经典问题项目原先运行在Oracle JDK 8上但随着Oracle对JDK商业许可政策的调整以及项目本身有向容器化、云原生环境迁移的需求继续使用Oracle JDK在合规性和成本上开始变得不那么“优雅”。于是我决定将运行环境从Oracle JDK 8迁移到OpenJDK 17。这不仅仅是一个简单的“替换”操作背后涉及到许可证合规性、长期支持LTS版本选择、运行时兼容性验证以及开发工具链适配等一系列问题。如果你也在考虑为你的Java应用更换一个更自由、更现代的运行时环境或者正被“java: you aren‘t using a compiler supported by lombok”这类恼人的构建问题困扰那么这次从Oracle JDK到OpenJDK 17的完整迁移实录或许能给你提供一个清晰的路线图。OpenJDK作为Java SE规范的开源参考实现如今已成为绝大多数生产环境的首选。它不仅完全免费而且由包括Red Hat、Amazon、Azul等在内的多家厂商提供商业支持和服务。迁移的核心价值在于规避潜在的商业许可风险、拥抱更长的免费支持周期如OpenJDK 17的LTS支持到2029年、以及获得更好的容器兼容性和性能优化。这次迁移的目标是确保应用在切换JDK供应商和版本后功能完全一致性能稳定且构建、部署流程无缝衔接。2. 迁移前的核心考量与准备工作在动手替换二进制文件之前充分的评估和准备是成功迁移的一半。盲目操作很可能导致应用在运行时出现各种难以排查的类加载、API行为差异或性能回退问题。2.1 版本与发行版选型为什么是OpenJDK 17首先需要决定迁移到哪个版本。直接从JDK 8跳到JDK 17是一个跨度较大的升级但这也是目前业界的主流推荐路径。跳过中间非LTS版本JDK 9到JDK 16是非长期支持版本它们的支持周期很短不适合用于生产环境。JDK 11和JDK 17是紧接在JDK 8之后的LTS版本。选择JDK 17而非JDK 11的理由虽然JDK 11也是一个优秀的LTS版本但JDK 17带来了更多成熟的、对生产环境有益的特性并且其支持周期更长。例如JDK 17中密封类Sealed Classes、模式匹配Pattern Matching等特性已经稳定并且它在容器内存和启动速度方面有更多优化。从社区生态来看越来越多的开源库和框架已经将主要兼容性测试转向了JDK 17。因此除非有非常强的遗留库绑定在JDK 11上否则直接选择JDK 17是更面向未来的决定。选择哪个OpenJDK发行版OpenJDK本身是一个源码项目我们需要选择由某个组织构建并分发的二进制版本。常见的有Eclipse Temurin由Eclipse基金会旗下的Adoptium项目提供是目前社区最受推崇的免费发行版之一提供清晰的许可和长期支持。Amazon Corretto亚马逊提供的免费、多平台、生产就绪的发行版亚马逊内部服务大量使用可靠性高。Azul ZuluAzul Systems提供的免费发行版也有对应的商业支持版本。它的下载渠道和版本非常全面。Oracle OpenJDKOracle官方构建的OpenJDK但请注意它的免费更新只提供到下一个版本发布。对于LTS版本Oracle不提供免费的长期安全更新你需要付费订阅才能获得。因此不推荐将Oracle OpenJDK用于需要长期安全维护的生产环境。注意对于生产环境我强烈推荐使用Eclipse Temurin或Amazon Corretto。它们都提供对LTS版本如JDK 17的免费安全更新直至该版本生命周期结束。本次迁移我选择了Eclipse Temurin 17。2.2 环境与依赖盘点知己知彼替换JDK不是孤立的它会影响整个工具链。在开始前请系统性地检查以下内容操作系统与环境变量记录当前JAVA_HOME的路径和PATH中Java命令的优先级。在Linux/macOS上使用which java和java -version在Windows上检查系统环境变量。同时检查是否有脚本如启动脚本、CI/CD流水线脚本硬编码了JDK路径。构建工具与IDEMaven/Gradle检查pom.xml或build.gradle中是否通过maven-toolchains-plugin或gradle toolchains指定了特定的JDK供应商和版本。同时确认maven-compiler-plugin的source和target版本设置。集成开发环境如IntelliJ IDEA或Eclipse需要确认项目SDK设置指向新的OpenJDK 17路径。应用依赖的三方库这是兼容性风险最高的部分。使用mvn dependency:tree或gradle dependencies命令导出完整的依赖树。重点关注那些与JDK内部API如sun.misc.*、字节码操作如ASM版本、或特定于Oracle JDK实现如某些JVM参数或JMX MBean相关的库。常见的“危险”库包括低版本的CGLIB、ASM以及某些使用了com.sun.*包的库。应用代码自查在代码中搜索对com.sun.*、sun.*、oracle.*包的引用。这些是JDK的内部API在OpenJDK中可能不存在、行为不同甚至被完全移除如JDK 9模块化之后。同时检查是否使用了javax.xml.bind、javax.activation等已在JDK 11中被移除的Java EE模块这些需要额外添加依赖如jakarta.xml.bind:jakarta.xml.bind-api。2.3 建立测试与回滚基线在修改任何东西之前必须确保你能衡量迁移的成功与否并且在出现问题时能快速恢复。性能与功能基线在现有的Oracle JDK 8环境下运行一遍完整的自动化测试套件单元测试、集成测试并记录通过率。如果有可能对核心接口进行一轮压力测试或基准测试记录关键的TPS、平均响应时间、GC停顿时间等指标。这将作为迁移后的对比基准。备份与回滚方案备份当前的JDK安装目录、项目配置文件pom.xml, build.gradle, .idea/等、以及重要的环境配置。确保你可以在几分钟内通过替换回原JDK和配置文件将环境恢复原状。搭建隔离的测试环境最好能在独立的开发机器、虚拟机或容器中先进行迁移测试避免污染主开发环境。3. 分步迁移实操全记录准备工作就绪后我们就可以开始动手了。整个过程我会分为开发环境、构建环境和生产环境三个场景来详细说明。3.1 步骤一下载并安装OpenJDK 17以在Linux服务器和Windows开发机上安装Eclipse Temurin 17为例。对于Linux (Ubuntu/CentOS) 推荐使用包管理器安装便于后续更新和管理。# 对于Ubuntu/Debian首先添加Adoptium的APT仓库 sudo apt install -y wget apt-transport-https gnupg wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | sudo apt-key add - echo deb https://packages.adoptium.net/artifactory/deb $(awk -F /^VERSION_CODENAME/{print$2} /etc/os-release) main | sudo tee /etc/apt/sources.list.d/adoptium.list sudo apt update sudo apt install temurin-17-jdk # 安装后验证版本 java -version对于Windows访问 Eclipse Temurin官网 。选择版本“17”操作系统“Windows”架构“x64”包类型“JDK”然后下载.msi安装程序。运行安装程序。建议安装路径不要包含空格例如C:\dev\java\jdk-17-temurin。安装完成后需要手动配置环境变量如果安装程序没有自动配置JAVA_HOME:C:\dev\java\jdk-17-temurin在Path变量中添加%JAVA_HOME%\bin在终端中验证java -version输出应类似于openjdk version 17.0.11 2024-04-16 Eclipse Temurin(TM) JDK 17.0.119 ...3.2 步骤二配置开发工具链IntelliJ IDEA配置打开IDEA进入File - Project Structure (CtrlAltShiftS)。在Project设置页将Project SDK和Project language level都改为 “17”。在Modules设置页确保每个模块的Language level也是 “17”。进入File - Settings - Build, Execution, Deployment - Build Tools - Maven(或Gradle)。将Maven home path下的Runner标签页中的JRE改为新安装的OpenJDK 17。对于Gradle在Build and run using和Run tests using中选择 IDEA自带的Gradle JVM或指定为OpenJDK 17。Maven项目配置 在项目的pom.xml中显式地配置Maven编译器插件确保编译目标与JDK 17一致。properties maven.compiler.source17/maven.compiler.source maven.compiler.target17/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties build plugins plugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-compiler-plugin/artifactId version3.11.0/version !-- 使用较新版本以更好支持JDK 17 -- configuration source17/source target17/target !-- 如果使用Lombok等注解处理器需要显式配置 -- annotationProcessorPaths path groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version1.18.30/version !-- 使用与JDK 17兼容的Lombok版本 -- /path /annotationProcessorPaths /configuration /plugin /plugins /build这个配置能从根本上解决 “java: you aren‘t using a compiler supported by lombok” 错误。该错误通常是因为Maven使用了与项目SDK不同的JRE来运行注解处理器显式配置annotationProcessorPaths可以强制Maven使用正确的处理器。3.3 步骤三解决依赖与代码兼容性问题这是迁移中最可能“踩坑”的环节。在配置好工具链后首先尝试执行mvn clean compile。场景一缺失的Java EE模块如果遇到类似java.lang.ClassNotFoundException: javax.xml.bind.JAXBException的错误说明代码或某个依赖库使用了JDK 11中被移除的Java EE模块。解决方法是在pom.xml中添加对应的Jakarta EE依赖Java EE已捐赠给Eclipse基金会并重命名为Jakarta EE。dependency groupIdjakarta.xml.bind/groupId artifactIdjakarta.xml.bind-api/artifactId version4.0.1/version /dependency dependency groupIdcom.sun.xml.bind/groupId artifactIdjaxb-impl/artifactId version4.0.4/version scoperuntime/scope /dependency !-- 如果需要JAX-WS -- dependency groupIdjakarta.xml.ws/groupId artifactIdjakarta.xml.ws-api/artifactId version4.0.1/version /dependency场景二使用了JDK内部API如果编译或运行时报告访问了sun.misc.BASE64Encoder等受限API必须修改代码。JDK 9引入了模块化默认禁止访问大多数内部API。最佳实践是寻找标准的替代API。例如用java.util.Base64替换sun.misc.BASE64Encoder。如果暂时无法修改例如在第三方库中可以在启动JVM时添加参数来开放这些内部API的访问这仅是临时方案不推荐用于生产--add-opens java.base/sun.security.x509ALL-UNNAMED --add-opens java.base/sun.security.utilALL-UNNAMED你需要根据错误信息将对应的模块和包名添加到--add-opens参数中。场景三依赖库版本过旧某些库的老版本可能与JDK 17不兼容。需要升级。常见需要检查的库包括ASM字节码操作框架至少需要9.x版本。通常由Spring、MyBatis等框架间接引入。CGLIB动态代理库需要3.3.0或更高版本。JUnit确保使用JUnit 5junit-jupiter。各种连接池、驱动检查数据库驱动如MySQL Connector/J、Redis客户端如Lettuce、HTTP客户端等是否有针对JDK 17的更新版本。可以使用Maven命令检查依赖冲突和过时依赖mvn versions:display-dependency-updates mvn dependency:tree -Dverbose3.4 步骤四运行测试与基准验证当项目能够成功编译后立即运行所有测试。mvn clean test仔细观察测试结果。除了功能测试要特别关注那些涉及序列化/反序列化、反射、动态代理、本地方法JNI以及日期时间处理的测试用例这些是跨JDK版本最容易出现行为差异的领域。如果测试全部通过恭喜你迁移的核心难关已经攻克。接下来可以尝试打包并启动应用进行集成测试。mvn clean package java -jar target/your-application.jar在应用运行后执行一系列核心业务操作确保功能正常。最后如果你在准备阶段建立了性能基线现在可以运行同样的基准测试对比关键指标GC频率、内存使用、吞吐量。通常从JDK 8升级到JDK 17得益于新的G1GC垃圾回收器的成熟和JIT编译器的优化应用性能会有一定提升尤其是启动速度和内存效率。3.5 步骤五生产环境部署生产环境的部署策略取决于你的发布流程。传统服务器部署与开发环境类似在服务器上安装OpenJDK 17更新所有启动脚本中的JAVA_HOME和PATH指向新JDK。然后部署新的应用包。务必先在预发布环境Staging进行完整验证。容器化部署这是最推荐的方式它能将JDK和环境与应用一起打包确保环境一致性。# 使用官方的Eclipse Temurin 17作为基础镜像 FROM eclipse-temurin:17-jre-jammy AS runtime # 设置工作目录 WORKDIR /app # 复制应用jar包 COPY target/your-application.jar app.jar # 设置JVM参数例如使用G1GC并优化容器内内存感知 ENV JAVA_OPTS-XX:UseG1GC -XX:MaxRAMPercentage75.0 -XX:UseContainerSupport # 启动命令 ENTRYPOINT [sh, -c, java $JAVA_OPTS -jar app.jar]注意-XX:UseContainerSupport和-XX:MaxRAMPercentage参数它们能让JVM更好地感知容器内存限制避免被OOMKiller杀死。这也是解决“java: outofmemoryerror: insufficient memory”错误在容器环境中的关键配置。4. 迁移后常见问题排查与优化即使通过了测试在生产环境也可能遇到一些独特的问题。这里记录几个我遇到或常见的“坑”。4.1 问题一Lombok注解在IDE中不生效但Maven编译正常这是一个经典的IDE与构建工具不同步的问题。原因IntelliJ IDEA默认使用自身的增量编译机制可能没有正确识别Maven配置的注解处理器路径。解决方案在IDEA中确保已安装并启用了Lombok插件。进入File - Settings - Build, Execution, Deployment - Compiler - Annotation Processors。勾选Enable annotation processing。在Store generated sources relative to:下拉框中选择Module content root。点击OK然后对项目执行File - Invalidate Caches and Restart...重启IDEA。执行Build - Rebuild Project。4.2 问题二应用启动后出现UnsupportedClassVersionError错误信息类似Unsupported major.minor version 61.0。原因这表示你尝试用一个低版本的JRE去运行由高版本JDK编译的类文件。版本61对应的是JDK 17。这说明你的运行环境java命令指向的仍然是旧的JDK 8。排查在命令行执行java -version确认输出的是OpenJDK 17。检查你的启动脚本、服务配置文件如systemd unit文件、容器镜像的JAVA_HOME和PATH是否都已正确更新。如果你使用IDE运行检查Run/Debug Configuration中的JRE是否设置为OpenJDK 17。4.3 问题三性能调优参数差异从JDK 8的Parallel GC切换到JDK 17默认的G1 GC一些旧的JVM参数可能失效或需要调整。废弃参数-XX:UseConcMarkSweepGC(CMS) 在JDK 14中被移除-XX:UseParallelGC仍然可用但非默认。在JDK 17中G1GC是默认且推荐的选择。推荐的基础参数-XX:UseG1GC -XX:MaxRAMPercentage75.0 # 容器环境下非常有用 -XX:InitialRAMPercentage50.0 -XX:UseStringDeduplication # 减少重复字符串内存占用 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/path/to/dumps监控与进一步调优使用jcmd pid GC.heap_info、jstat -gc pid或可视化工具如VisualVM、JMC来观察GC行为再针对性调整-XX:MaxGCPauseMillis、-XX:G1NewSizePercent等参数。4.4 问题四第三方工具或监控代理兼容性一些APM工具如SkyWalking Agent、性能分析工具如Async-Profiler或安全代理可能需要特定版本的JDK支持。行动查阅这些工具的官方文档确认其明确支持OpenJDK 17。通常需要升级Agent到最新版本。在测试阶段就要将这些Agent挂载到应用上进行验证。迁移到OpenJDK 17不是一个一蹴而就的简单命令替换而是一个需要从许可证、版本、工具链、依赖、代码到部署环境进行通盘考虑的系统工程。整个过程最耗费时间的往往不是安装新JDK而是排查和解决那些因版本跨度带来的、深藏的兼容性问题。我的建议是为迁移计划预留充足的时间建立完善的测试和回滚机制采用分阶段、分模块的迁移策略。一旦完成迁移你收获的将不仅是一个合规、免费的环境更是一个为未来现代Java特性如虚拟线程、向量API等铺平道路的坚实基础。在容器和云原生时代使用一个积极维护、对容器友好的OpenJDK LTS发行版无疑是更明智的选择。