跳到主要内容

JEP 493:无需 JMOD 即可链接运行时图像

概括

通过启用该工具来创建自定义运行时映像而不使用 JDK 的 JMOD 文件,可将 JDK 的大小减少约 25% jlink。在构建 JDK 时必须启用此功能;默认情况下不会启用此功能,并且某些 JDK 供应商可能选择不启用它。

目标

允许用户从模块链接运行时图像,无论这些模块是独立的JMOD 文件模块化 JAR 文件,还是先前链接的运行时图像的一部分。

动机

在云环境中,文件系统上安装的 JDK 的大小非常重要,因为包含已安装 JDK 的容器镜像会通过网络自动且频繁地从容器注册表中复制。减小 JDK 的大小将提高这些操作的效率。

完整安装的 JDK 有两个主要组件:运行时映像(即可执行的 Java 运行时系统)和运行时映像中每个模块的一组_打包模块_(JMOD 格式) 。

jlink该工具在创建自定义运行时映像时使用 JMOD 文件。完整 JDK 中的运行时映像本身就是这样一个映像,它通过从这些 JMOD 文件创建jlink。因此,运行时映像中的每个类文件、本机库、配置文件和其他资源也存在于这些 JMOD 文件之一中 — 可以说是浪费了大量空间。

事实上,完整 JDK 中的 JMOD 文件约占 JDK 总大小的 25%。如果我们可以增强该jlink工具以从运行时映像本身中提取类文件、本机库、配置文件和其他资源,那么我们可以通过省略 JMOD 文件来大幅减少已安装 JDK 的大小。

描述

新的 JDK构建时配置选项--enable-linkable-runtime会构建一个 JDK,其jlink工具无需使用 JDK 的 JMOD 文件即可创建运行时映像。生成的 JDK 不包含这些文件,即没有jmods目录。因此,它比使用默认配置构建的 JDK 小约 25%,尽管它包含完全相同的模块。

$ configure [ ... other options ... ] --enable-linkable-runtime
$ make images

任何 JDK 版本中的工具jlink都可以使用 JMOD 文件和模块化 JAR 文件。此外,在启用此功能的 JDK 版本中,jlink可以使用它所属的运行时映像中的模块。--help的输出jlink显示它是否具有此功能:

$ 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指定。jlink``--module-path

使用新功能运行的用户体验jlink与不使用新功能运行的用户体验完全相同。如果我们想通过省略一些模块来减小运行时映像的大小,我们可以继续这样做,只包含我们需要的模块,但不需要 JMOD 文件。例如,要创建仅包含和模块jlink的运行时映像,调用方式相同: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指定它们:jlink``--module-path

$ 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
$

该工具从模块化 JAR 文件和中复制和模块jlink的类文件和资源。它从 JDK 的运行时映像中提取 JDK 模块的类文件、本机库、配置文件和其他资源。app``lib``app.jar``lib.jar

--verbose现在的选项显示jlink了每个模块的来源:

$ 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 构建是否包含此功能取决于该供应商。

我们可能会建议在未来的版本中默认启用此功能。

限制

jlink与使用默认配置构建的 JDK 中的工具相比,使用 构建的 JDK 中的工具有--enable-linkable-runtime一些限制:

  • jlink无法用于创建本身包含该jlink工具的运行时映像。该jlink工具位于jdk.jlink模块中,因此此操作失败:

    $ jlink --add-modules jdk.jlink --output image
    Error: This JDK does not contain packaged modules and cannot be used \
    to create another run-time image that includes the jdk.jlink module

    如果事实证明存在问题,我们可能会在未来重新审视这一限制。

  • jlink如果修改了任何用户可编辑的配置文件,则失败。

    JDK 的conf目录包含开发人员可以编辑以配置 JDK 的各种文件。该conf/security/java.security文件特别配置了安全提供程序、加密算法等。在默认构建中,jlink从 JDK 的 JMOD 文件复制 JDK 模块的用户可编辑配置文件。如果没有 JMOD 文件,jlink则从运行时映像复制配置文件,如果任何文件与原始文件不同,则失败:

    $ jlink --add-modules java.xml --output image
    Error: [...]/bin/conf/security/java.security has been modified

    此限制可防止jlink创建具有临时或不安全配置的运行时映像。如果安全配置已更改为启用默认禁用的过时消息摘要算法,则将该配置复制到新的运行时映像是不合适的。

  • 交叉链接(例如,jlink在 Linux/x64 上运行以创建 Windows/x64 的运行时映像)是不可能的。

  • --patch-module不支持从正在使用的运行时图像进行链接。

  • --module-path不支持通过从不同的运行时映像中提取模块进行链接(例如,通过将该映像指定给选项)。

替代方案

JDK 供应商可以将 JDK 的 JMOD 文件作为单独的下载提供。一些 Linux 发行版已经基本上做到了这一点,即为 JDK 运行时映像提供一个安装包,为相应的 JMOD 文件提供另一个安装包。

这种方法很脆弱,因为如果没有安装第二个包,那么该jlink工具将无法工作。此外,这种方法不太适合云环境,因为在云环境中,JDK 运行时映像及其 JMOD 文件可能会位于不同的、相互冲突的容器映像层中。