jlink
jlink 是 JDK 9 引入的自定义运行时镜像生成工具,基于 Java 平台模块系统(JPMS),可将应用所依赖的模块裁剪打包为最小化 JRE,无需分发完整 JDK/JRE。
核心概念
| 概念 | 说明 |
|---|---|
| 模块(module) | JPMS 的基本单元,每个模块声明依赖与导出(module-info.java) |
jmods/ | $JAVA_HOME/jmods 下存放各平台标准模块的 .jmod 文件 |
| 运行时镜像 | jlink 输出的自包含目录,含 bin/java、裁剪后的库和资源 |
基本用法
# 生成只含 java.base 的最小运行时
jlink \
--module-path $JAVA_HOME/jmods \
--add-modules java.base \
--output minimal-jre
# 生成含常用模块的运行时(适合 GUI/日志/网络应用)
jlink \
--module-path $JAVA_HOME/jmods \
--add-modules java.base,java.desktop,java.logging,java.net.http \
--output custom-jre \
--strip-debug \
--compress=2 \
--no-header-files \
--no-man-pages常用选项
| 选项 | 说明 |
|---|---|
--module-path <path> | 模块查找路径,通常指向 $JAVA_HOME/jmods |
--add-modules <modules> | 要包含的模块列表(逗号分隔),依赖会自动递归包含 |
--output <dir> | 输出目录(不能已存在) |
--strip-debug | 去除调试符号,减小体积 |
--compress=<0|1|2> | 压缩级别:0 不压缩,1 字符串共享,2 ZIP 压缩 |
--no-header-files | 不包含头文件(.h),进一步缩减体积 |
--no-man-pages | 不包含 man 手册页 |
--launcher <name>=<module>/<class> | 在 bin/ 中生成启动脚本 |
--save-opts <file> | 将本次参数保存到文件供复用 |
--list-modules | 列出输出镜像中包含的所有模块 |
自动推断模块依赖
使用 jdeps 工具先分析 JAR 所依赖的模块,再传给 jlink:
# 1. 分析 app.jar 依赖的 JDK 模块
jdeps --print-module-deps app.jar
# 示例输出:java.base,java.logging,java.sql
# 2. 将输出直接传给 jlink
jlink \
--module-path $JAVA_HOME/jmods \
--add-modules $(jdeps --print-module-deps app.jar) \
--output custom-jre \
--strip-debug \
--compress=2添加启动器
--launcher 在输出镜像的 bin/ 目录中生成可直接运行的脚本:
jlink \
--module-path $JAVA_HOME/jmods:mods \
--add-modules com.example.app \
--launcher myapp=com.example.app/com.example.Main \
--output app-jre
# 运行
app-jre/bin/myapp体积对比示例
| 内容 | 大小(参考) |
|---|---|
| 完整 JRE 21 | ~300 MB |
| jlink(java.base 仅基础模块) | ~40 MB |
| jlink + strip-debug + compress=2 | ~25 MB |
实际大小因模块数量和 JDK 版本而异。
与 jpackage 配合
jlink 生成的裁剪运行时可直接交给 jpackage 打包为原生安装程序,大幅缩小分发体积:
# 1. 裁剪运行时
jlink \
--module-path $JAVA_HOME/jmods \
--add-modules java.base,java.desktop,java.logging \
--output custom-jre \
--strip-debug \
--compress=2
# 2. 打包为原生安装程序
jpackage \
--name MyApp \
--runtime-image custom-jre \
--input lib/ \
--main-jar app.jar \
--main-class com.example.Main常见问题
Q: 运行时报 module not found 怎么办?
A: 使用 jdeps --print-module-deps 重新分析,确认所有依赖模块都已加入 --add-modules。
Q: 第三方 JAR 没有 module-info.java 怎么处理?
A: 非模块化 JAR 需放在 --module-path 的 classpath 部分,或通过 --add-modules ALL-MODULE-PATH 强制包含。