自从Oracle在JDK 11中引入模块化系统后,传统的JRE独立分发模式就逐渐退出历史舞台。作为一名长期从事Java应用部署的开发者,我深刻体会到在容器化时代,一个精简的运行时环境对应用部署有多么重要。想象一下,当你只需要运行一个简单的Spring Boot应用时,却不得不带着整个JDK的庞大身躯,这就像出门买菜却开着集装箱卡车一样荒谬。
Oracle JDK 17延续了这一变革,不再提供单独的JRE下载。但这反而给了我们更多灵活性——通过jlink工具,我们可以像定制西装一样,为每个应用量身打造只包含必要模块的运行时环境。在我的实际项目中,通过这种方式通常能将运行时体积减少40-60%,这对于需要频繁部署的微服务架构来说意义重大。
jlink是JDK 9引入的模块化链接工具,它允许开发者通过组合Java模块来创建优化的运行时镜像。其核心参数包括:
--module-path:指定查找模块的路径,通常是JDK安装目录下的jmods文件夹--add-modules:明确指定需要包含的模块--output:定义生成的JRE输出目录一个典型的jlink命令如下:
bash复制$JAVA_HOME/bin/jlink \
--module-path $JAVA_HOME/jmods \
--add-modules java.base,java.logging \
--strip-debug \
--no-header-files \
--no-man-pages \
--compress=2 \
--output /custom/jre
确定应用依赖哪些模块是个技术活。这里推荐使用jdeps工具进行静态分析:
bash复制jdeps --ignore-missing-deps -q --print-module-deps your-app.jar
这个命令会输出逗号分隔的模块列表。但要注意,对于使用反射或服务加载器的代码,静态分析可能不够全面。在我的经验中,Spring应用通常还需要额外添加以下模块:
下面展示一个经过实战检验的多阶段Dockerfile示例:
dockerfile复制# 构建阶段
FROM eclipse-temurin:17-jdk as builder
WORKDIR /app
COPY target/myapp.jar .
# 分析依赖并构建最小JRE
RUN jdeps --ignore-missing-deps -q \
--print-module-deps myapp.jar > /tmp/modules.txt && \
jlink \
--add-modules $(cat /tmp/modules.txt),jdk.crypto.ec \
--strip-debug \
--no-man-pages \
--no-header-files \
--compress=2 \
--output /jre
# 运行阶段
FROM debian:11-slim
COPY --from=builder /jre /opt/jre
COPY --from=builder /app/myapp.jar /app/
ENV JAVA_HOME=/opt/jre
ENV PATH="$JAVA_HOME/bin:$PATH"
ENTRYPOINT ["java", "-jar", "/app/myapp.jar"]
这个方案的关键优势在于:
在实际部署中可能会遇到以下典型问题:
问题1:NoClassDefFoundError或ModuleNotFoundException
java --list-modules检查可用模块问题2:反射调用失败
--bind-services参数,或手动添加相关模块问题3:性能下降
--compress=1或移除压缩参数根据我的项目经验,以下模块组合适用于大多数Java应用场景:
| 应用类型 | 核心模块 |
|---|---|
| 基础CLI工具 | java.base |
| Web应用 | java.base,java.logging,java.sql,java.naming,jdk.httpserver |
| Spring Boot | java.base,java.logging,java.sql,java.naming,java.management,jdk.crypto.ec |
| 图形界面应用 | java.base,java.desktop,java.datatransfer |
安全模块:即使应用不直接使用加密功能,也建议添加jdk.crypto.ec模块,因为很多安全协议依赖它
诊断工具:在测试镜像中可以保留jdk.jcmd模块,方便使用jcmd进行诊断
本地化支持:如果需要多语言支持,记得添加jdk.localedata模块
服务绑定:使用--bind-services参数可以自动包含服务提供者模块
使用Oracle JDK构建自定义JRE时需特别注意:
相比之下,OpenJDK及其衍生版本(如Eclipse Temurin、Amazon Corretto)通常采用更宽松的许可。在我的生产环境中,更推荐使用这些开源替代方案。
重要提示:从JDK 17开始,Oracle对非LTS版本的支持周期缩短至6个月。对于生产环境,建议选择LTS版本(如17、21)或使用提供长期支持的发行版。