基于JDK17及以上版本,制作内置JRE的可执行应用。

一、使用jdepsjmod寻找模块依赖

jdeps \
--print-module-deps \
--ignore-missing-deps \
-recursive \
target/app.jar

jdeps无法统计部分依赖,如:非模块化的依赖、动态加载或反射的依赖等等。可以使用下述jmod脚本手工找到对应的依赖:

JAVA_HOME=/usr/lib/jvm/java-21-openjdk
CLASS=javax.management.NotificationEmitter

find $JAVA_HOME/jmods -name "*.jmod" -exec sh -c "jmod list {} | grep -q $CLASS && echo {}" \;

二、使用jlink生成自定义JRE

JAVA_HOME=/usr/lib/jvm/java-21-openjdk
MOUDLES=java.base # 填入上述统计的模块依赖

jlink \
--output jre \
--module-path $JAVA_HOME/jmods \
--add-modules $MOUDLES \
--strip-debug \
--no-header-files \
--no-man-pages
  • --output:指定输出目录
  • --module-path:指定模块路径
  • --add-modules:指定要包含的模块,可使用$(java --list-modules | cut -d "@" -f 1 | tr "\n" ",")获取所有jmod
  • --strip-debug:移除调试信息,大幅缩减JRE体积
  • --no-header-files/--no-man-pages:移除include中的c++头文件和man中的帮助文档文件

可使用jimage查看目标JRE中的所有class:

jimage list jre/lib/modules

三、使用jpackage打包应用

jpackage \
--input target \
--main-jar app.jar \
--type app-image \
--name app \
--dest output \
--runtime-image jre
  • --input:指定包含应用程序文件的输入目录,jpackage会从这个目录中查找要打包的文件
  • --main-jar:指定主JAR文件,如果JAR的MANIFEST.MF已指定Main-Class,可省略--main-class参数,如:SpringBoot打包后的FatJar。
  • --type:指定安装包类型,可选值:app-image、deb、rpm、exe、msi、pkg
  • --name:指定应用程序名称。不显性指定时,默认为Main-Class对应的值
  • --dest:指定输出目录。不显性指定时,默认为当前目录
  • --runtime-image:指定自定义的JRE运行时。不显性指定时,默认使用未经过裁剪的JRE