JDK9开始就没有JRE,JDK 9和更高版本提供了jlink命令,以将一组模块及其依赖项组装和优化到自定义JRE Runtime中。本文主要介绍解决JDK11安装后不自带jre的问题,使用jlink生成创建JRE Runtime。

java --list-modules:获取所有可用openjdk模块的列表。

jlink --no-header-files --no-man-pages --compress=2 --add-modules <module-list from step 1> --output java-runtime:创建一个精简的JRE

--module-path:等效于类路径,告诉JVM在哪里搜索模块。

--add-modules:这指定要解决的其他根模块。由于所有应用程序代码都在类路径上,因此我们没有初始模块,因此此选项是必需的。我们可以用两种方式指定它,要么使用上述的显式模块列表,要么使用parameter指示要包含模块路径上的所有内容ALL-MODULE-PATH

1、OpendJDK 12创建JRE命令

jlink --no-header-files --no-man-pages --compress=2 --add-modules java.base,java.compiler,java.datatransfer,java.desktop,java.instrument,java.logging,java.management,java.management.rmi,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.se,java.security.jgss,java.security.sasl,java.smartcardio,java.sql,java.sql.rowset,java.transaction.xa,java.xml,java.xml.crypto,jdk.accessibility,jdk.aot,jdk.attach,jdk.charsets,jdk.compiler,jdk.crypto.cryptoki,jdk.crypto.ec,jdk.crypto.mscapi,jdk.dynalink,jdk.editpad,jdk.hotspot.agent,jdk.httpserver,jdk.internal.ed,jdk.internal.jvmstat,jdk.internal.le,jdk.internal.opt,jdk.internal.vm.ci,jdk.internal.vm.compiler,jdk.internal.vm.compiler.management,jdk.jartool,jdk.javadoc,jdk.jcmd,jdk.jconsole,jdk.jdeps,jdk.jdi,jdk.jdwp.agent,jdk.jfr,jdk.jlink,jdk.jshell,jdk.jsobject,jdk.jstatd,jdk.localedata,jdk.management,jdk.management.agent,jdk.management.jfr,jdk.naming.dns,jdk.naming.rmi,jdk.net,jdk.pack,jdk.rmic,jdk.scripting.nashorn,jdk.scripting.nashorn.shell,jdk.sctp,jdk.security.auth,jdk.security.jgss,jdk.unsupported,jdk.unsupported.desktop,jdk.xml.dom,jdk.zipfs --output java-runtime

2、JDK 11按需生成的JRE Runtime

java -jar FlightRecorder.jar为例,按照能正常运行的需要来生成JRE Runtime,使用JRE Runtime 尽量精简。

1) JDK9之后运行java -jar FlightRecorder.jar报错

Error: Could not find or load main class flightrecorder.Main
Caused by: java.lang.NoClassDefFoundError: javafx/application/Application

由于Oracle JDK和OpenJDK二进制文件都不包含JavaFX。幸运的是,Gluon的好人免费提供了OpenJFX项目的构建,因此很容易解决这个问题。

2) 下载JavaFX SDK使用下面命令执行java -jar FlightRecorder.jar

JavaFX SDK下载地址https://gluonhq.com/products/javafx/

java --module-path /opt/javafx-sdk-11/lib --add-modules=javafx.controls -jar FlightRecorder.jar

3) 使用jdeps命令查看依赖的模块

在应用程序上使用JDK 9版本的jdeps,则会得到以下结果

> jdeps --list-deps FlightRecorder.jar
java.base
java.logging
java.sql
javafx.base
javafx.controls
javafx.graphics
not found
unnamed module: FlightRecorder.jar

Jdeps在JDK 10中得到了改进,如果我们使用JDK 11版本,则会得到以下信息

> jdeps --list-deps FlightRecorder.jar
java.base
java.logging
java.sql

尽管显示应用程序使用了哪些模块,但还缺少一些信息。要单独提供JavaFX模块,需要分析它们可能具有的任何JDK模块依赖关系。解决方案是以与Java launcher相同的方式包括模块详细信息

> jdeps --module-path /opt/javafx-sdk-11/lib --add-modules=javafx.controls --list-deps FlightRecorder.jar
JDK removed internal API/com.sun.media.jfxmediaimpl.platform.ios
java.base
java.datatransfer
java.desktop/java.awt.dnd.peer
java.desktop/sun.awt
java.desktop/sun.awt.dnd
java.desktop/sun.swing
java.logging
java.scripting
java.sql
java.xml
jdk.jsobject
jdk.unsupported
jdk.unsupported.desktop
jdk.xml.dom

4) 生成JRE Runtime

为了使运行时大小尽可能小,我们使用命令行标志来禁止包含头文件和手册页,以及剥离调试信息并在相关时压缩为zip文件格式。要包括的模块列表是jdeps生成的模块。

jlink --no-header-files --no-man-pages --add-modules java.datatransfer,java.desktop,java.logging,java.management,java.naming,java.rmi,java.scripting,java.sql,java.transaction.xa,java.xml,jdk.jsobject,jdk.unsupported,jdk.unsupported.desktop,jdk.xml.dom --output java-runtime

5) 使用创建好JRE执行命令

java-runtime/bin/java --module-path /opt/javafx-sdk-11/lib --add-modules=javafx.controls -jar FlightRecorder.jar

相关文档

jlink命令文档https://docs.oracle.com/javase/9/tools/jlink.htm#JSWOR-GUID-CECAC52B-CFEE-46CB-8166-F17A8E9280E9

Using jlink to Build Java Runtimes for non-Modular Applications 

jdeps命令文档https://docs.oracle.com/javase/9/tools/jdeps.htm#JSWOR690

推荐文档