JEP 493: Linking Run-Time Images without JMODs | 不使用 JMOD 链接运行时镜像
摘要
通过启用 jlink
工具创建自定义运行时镜像而无需使用 JDK 的 JMOD 文件,将 JDK 的大小减少约 25%。此功能在构建 JDK 时必须启用;它不会默认启用,并且一些 JDK 供应商可能会选择不启用它。
目标
允许用户链接来自模块的运行时镜像,无论这些模块是独立的 JMOD 文件、模块化 JAR 文件,还是先前链接的运行时镜像的一部分。
动机
在云环境中,JDK 在文件系统上的安装大小非常重要,因为包含已安装 JDK 的容器镜像会自动且频繁地从容器注册表通过网络复制。减少 JDK 的大小可以提高这些操作的效率。
一个完整安装的 JDK 有两个主要组件:一个 运行时镜像,即可执行的 Java 运行时系统,以及一组以 JMOD 格式 打包的 模块,用于运行时镜像中的每个模块。
当创建 自定义运行时镜像 时,JMOD 文件被 jlink
工具使用。完整 JDK 中的运行时镜像本身就是这样一个镜像,它是通过 jlink
从这些 JMOD 文件创建的。因此,运行时镜像中的每一个类文件、本地库、配置文件和其他资源也存在于这些 JMOD 文件之一中——可以说这是一种巨大的空间浪费。
实际上,完整 JDK 中的 JMOD 文件约占 JDK 总大小的 25%。如果我们能够增强 jlink
工具直接从运行时镜像提取类文件、本地库、配置文件和其他资源,那么我们可以通过省略 JMOD 文件显著减少已安装 JDK 的大小。
说明
新的 JDK构建时 配置选项 --enable-linkable-runtime
构建了一个 JDK,其 jlink
工具可以无需使用 JDK 的 JMOD 文件创建运行时镜像。生成的 JDK 不包括那些文件,即没有 jmods
目录。因此,与使用默认配置构建的 JDK 相比,它的大小大约减少了 25%,尽管它包含完全相同的模块。
$ configure [ ... 其他选项 ... ] --enable-linkable-runtime
$ make images
任何 JDK 构建中的 jlink
工具都可以消费 JMOD 文件和模块化 JAR 文件。此外,在启用了此功能的 JDK 构建中,jlink
可以从其所属的运行时镜像消费模块。jlink
的 --help
输出显示它是否具有这种能力:
$ jlink --help
Usage: jlink <options> --module-path <modulepath> --add-modules <module>[,<module>...]
...
Capabilities:
Linking from run-time image enabled
$
这意味着正在使用的 jlink
工具可以从包含的运行时镜像链接 JDK 模块。如果它不具备这种能力,则会显示 Linking from run-time image disabled
。
拥有新能力的 jlink
版本总是优先从模块路径上的 JMOD 文件(如果可用)消费 JDK 模块。仅当模块路径上找不到 java.base
模块时,它才会从其所属的运行时镜像消费模块。其他任何模块仍然需要通过 --module-path
选项指定给 jlink
。
使用具备新能力的 jlink
运行的用户体验与不具备该能力的 jlink
运行体验完全相同。如果想要通过省略某些模块来减小运行时镜像的大小,我们可以继续只包含所需的模块,而不需要 JMOD 文件。例如,要创建只包含 java.xml
和 java.base
模块的运行时镜像,jlink
调用是相同的:
$ jlink --add-modules java.xml --output image
$ image/bin/java --list-modules
java.base@24
java.xml@24
$
jlink
的输出与从 JMOD 文件链接模块时完全相同。生成的运行时镜像比完整的 JDK 运行时镜像小约 60%。
对于更复杂情况的调用也是相同的。例如,假设我们想创建一个包含应用程序模块 app
及其所需库 lib
的运行时镜像。这些模块作为 模块化 JAR 文件 位于 mlib
目录中。我们通常通过 --module-path
选项将其指定给 jlink
:
$ ls mlib
app.jar lib.jar
$ jlink --module-path mlib --add-modules app --output app
$ app/bin/java --list-modules
app
lib
java.base@24
$
jlink
工具从模块化 JAR 文件 app.jar
和 lib.jar
中复制了 app
和 lib
模块的类文件和资源。它从 JDK 的运行时镜像中提取 JDK 模块的类文件、本地库、配置文件和其他资源。
jlink
的 --verbose
选项现在显示每个模块的来源:
$ ls custom-jmods
foo.jmod
$ jlink --add-modules foo \
--module-path=custom-jmods \
--verbose \
--output foo-image
Linking based on the current run-time image
java.base jrt:/java.base (run-time image)
foo file:///path/to/custom-jmods/foo.jmod
Providers:
java.base provides java.nio.file.spi.FileSystemProvider used by java.base
$
这里,模块 java.base
是从当前运行时镜像中提取的,而模块 foo
是从 JMOD 文件 foo.jmod
链接来的。
不默认启用
默认的构建配置将保持现状:生成的 JDK 将包含 JMOD 文件,且其 jlink
工具将无法在没有这些文件的情况下运行。您从首选供应商处获得的 JDK 构建是否包含此功能取决于该供应商。
我们可能会在未来发布的版本中提议默认启用此功能。
限制
使用 --enable-linkable-runtime
选项构建的 JDK 中的 jlink
工具与使用默认配置构建的 JDK 相比有一些局限性:
jlink
不能用于创建本身包含jlink
工具的运行时镜像。jlink
工具位于jdk.jlink
模块中,因此以下操作会失败:bash$ jlink --add-modules jdk.jlink --output image 错误:此 JDK 不包含打包的模块,并且无法用于创建包含 jdk.jlink 模块的另一个运行时镜像
如果这被证明存在问题,我们将来可能会重新审视这一限制。
如果修改了任何用户可编辑的配置文件,
jlink
将会失败。JDK 的
conf
目录包含了开发人员可能编辑以配置 JDK 的各种文件。特别是conf/security/java.security
文件,它配置了安全提供者、加密算法等。在默认构建中,jlink
从 JDK 的 JMOD 文件复制 JDK 模块的用户可编辑配置文件。没有 JMOD 文件,jlink
从运行时镜像复制配置文件,并在任何文件与原始文件不同的情况下失败:bash$ jlink --add-modules java.xml --output image 错误:[...]/bin/conf/security/java.security已被修改
此限制防止
jlink
创建具有临时或不安全配置的运行时镜像。如果安全配置被更改,例如启用了默认禁用的过时消息摘要算法,则将该配置复制到新的运行时镜像是不合适的。跨平台链接,例如,在 Linux/x64 上运行
jlink
以创建 Windows/x64 的运行时镜像,是不可能的。来自本身使用
--patch-module
选项的运行时镜像进行链接是不受支持的。通过从不同的运行时镜像中提取模块进行链接,例如,通过将该镜像指定给
--module-path
选项,是不受支持的。
替代方案
JDK 供应商可以单独提供 JDK 的 JMOD 文件作为独立下载。一些 Linux 发行版已经基本上做到了这一点,它们为 JDK 运行时镜像和相应的 JMOD 文件分别提供了安装包。
这种方法较为脆弱,因为如果未安装第二个软件包,则 jlink
工具将无法工作。此外,这种方法不适合云环境,在这种环境中,JDK 运行时镜像及其 JMOD 文件可能会最终位于不同且冲突的容器镜像层中。