为什么Java9引入JMOD文件格式?

Why did Java 9 introduce the JMOD file format?

Java9将编译后的代码打包到文件中的三种方式:

JIMAGE 针对速度进行了优化,space 并在运行时由 JVM 使用,因此引入 JIMAGE 是有道理的。 JIMAGE 文件不应发布到 Maven 存储库或在编译或 link 时使用。

文档声称 JMOD 可以存储本机代码和 JAR 文件无法存储的其他内容,并且开发人员可以制作和分发他们自己的 JMOD 文件。 JDK 附带 jmods/ 目录,其中包含 JDK 的所有模块供用户依赖。

问题:

这里引用了一些来自 JEP 261: Module System 的内容,其中包含关于 JMOD 文件的部分。

为什么?

来自JEP 261

The new JMOD format goes beyond JAR files to include native code, configuration files, and other kinds of data that do not fit naturally, if at all, into JAR files.

The final format of JMOD files is an open issue, but for now it is based on ZIP files.

开发者应该发布 JMOD 文件吗?

请注意,JMOD 文件似乎是一种在编译时和 link 时合并本机代码(以及其他内容)的方法。来自 JEP 261:

JMOD files can be used at compile time and link time, but not at run time.

(老实说,我不确定 JDK 9 之前的原生代码是如何发布的。)对于绝大多数开发人员(没有原生库或其他极端情况),我们只会发布模块化 jar。

JMOD 的目的没有很好的记录,现有的文档相当稀少。根据我的理解,这是对系统的in-depth解释。

警告:此答案的部分内容相当长、冗长、部分多余且难以阅读。非常欢迎建设性的、结构性的或语法上的编辑,以提高未来读者的可读性。


简短(呃)答案

Java 9 的新模块系统,Project Jigsaw, introduces the notion of a new optional link time phase, which occurs when the using the CLI tool jlink 构建自定义 space-optimized JRE。 jlink 将所有 explicit/transitive JAR modules/JMOD 依赖项捆绑到一个缩小的 JRE 中;依赖关系图中所有其他无法访问的依赖关系(从指定的根模块开始) not 捆绑到内置的 JRE 中。从 JDK 9+ 开始,Java 的所有标准库都已分解为 JMOD,位于 <jdk>/jmods.

JAR 只能包含 .class 和资源文件,而 JMOD(即 .jmod 文件)包含额外的文件,这些文件专门用于新的可选 link time 阶段自定义 JRE(例如可执行文件、本机库、配置、合法许可证等)。这些附加文件在 运行 时间在类路径中不可用作资源,而是安装在构建的 JRE 中的不同位置(例如,可执行文件和本机库位于 <jre>/bin 下)。从相关的捆绑 JAR 和 JMOD 依赖项,类 和文件资源将被写入一个优化的 JIMAGE 文件,位于 <jre>/lib/modules(替换 Java 8 和之前的 <jre>/lib/rt.jar版本)。 JMODs的作用是在编译时和link时,不是设计用于运行时

对于平均 library/application,应该只构建和推送 JAR,而不是 JMOD;只有在某些条件下,JMOD 才会提供 link time 阶段所需的关键功能。在撰写本文时,除了 alpha 发布插件 org.apache.maven.plugins:maven-jmod-plugin.

之外,Maven 似乎并未提供对 JMOD 的强大支持

长答案

这个 long-winded 答案的动机更为复杂,并阐明了新模块系统的基本运作方式。 post 中非常强调 CLI 工具 jlink,因为 JMOD 是专门为这个新的可选 link time 阶段设计的工具介绍。

拼图项目介绍

Java 9 引入 Project Jigsaw in 'JEP 261: Module System', a novel module system that can be used to minimize startup times and the size of JREs. As part of this release, the CLI utilities jmod, jimage, and jlink 与 JMODs/.jmods (ZIP-based) 和 JIMAGEs/.jimages 的新文件格式一起被引入。

这个新模块系统的一个重要收获是 CLI 工具 jlink 使开发人员能够构建一个自定义 JRE,其中仅包含相关的标准库和应用程序的外部依赖项。这在 compile time -> run time 管道中的传统阶段之间引入了可选 link 时间 阶段的新概念。

举一个使用 jlink 的优势的例子,一个从 JDK 15 构建的极简 JRE 只有 java.base 模块的大小约为 40MB,简而言之与 JDK 15 的 ~310MB 大小并列。这对于运送最小的自定义 JRE 特别有用,例如精益 Docker 图像。新的模块系统为 Java 生态系统带来了显着的好处,这在其他地方已经进行了详细讨论,因此这里不再详细阐述。

3 个 J:JAR、JMOD 和 JIMAGE

JAR、JMOD 和 JIMAGE 的高级描述并不能很快地解释清楚这三种文件格式的作用。以下是每个用途的 non-exhaustive 概述:

  • JARs: 基于 ZIP 文件格式的经典格式,用于将 类 和资源捆绑到 [ 的类路径中=372=]时间。这是自 1997 年 JDK 1.1 以来提出的 de-facto 主流标准。可以使用 java -cp/-classpath 标志将 JAR 添加到类路径中。几乎每个库或依赖项 hasiswill 都会使用这种格式,因此它被掩盖了本节结束。

  • JMODs: 一种基于 ZIP 文件格式的新格式,用于捆绑 JAR 可以包含的相同内容,但支持附加文件(例如可执行文件、本机库、配置、合法许可证等)在构建自定义 JRE 时在可选 link time 阶段消耗。 JMOD 设计用于编译时和 link 时,但 而不是 在 运行 时。可能引入了这种新格式(而不是扩展 JAR),因为这种新 archive-based 格式中的目录具有特殊含义,即 not 向后兼容已经使用的 JAR相同的目录名称。

    • JMOD 可以从 JAR 模块构建(即 con使用 CLI 工具 jmod.
    • 获得有效 module-info.class)
    • 从 JDK 9 开始,所有 Java 标准模块都存储在 JDK 安装中的 <jdk>/jmods 下。
    • 可以发布 JMOD 以供其他开发人员和上游应用程序使用;在撰写本文时,我不确定是否可以将 JMOD 推送到 Maven 存储库,但各种来源似乎表明暂时不可能。
    • JMOD 类 和资源 不能运行 时间 在类路径中使用 java -cp/-classpath 标志,因为 JMOD 存档中的 类 和资源存储在 classes 下而不是存档根目录中。

Note: There may be a way to add easily JMODs to the classpath at run time; however, research did not explicitly state any functionality relating to this. Merely adding a JMOD to the classpath will not be sufficient for using the classes and resources. A custom ClassLoader could be used to resolve class and resource files correctly in the JMOD archive at run time, however; this is generally not recommended and is not the purpose of JMODs.

  • JIMAGEs:在“JEP 220: Modular Run-Time Images”中引入的一种特殊文件格式,它是 运行time 图像包含 JRE(即标准库)的所有必要 类 和资源。在 JRE/JDK 9 之前,使用了一个大型 non-modular uber JAR,位于 <jre>/lib/rt.jar;它已被删除,取而代之的是存储在 <jre>/lib/modules 的单个优化 JIMAGE。此格式 而非 基于 ZIP 格式,并使用自定义格式,该格式比原始遗留 JAR 格式更省时且 space 高效,减少了启动时间。
    • 使用 CLI 工具 jlink 构建自定义 JRE 映像时,所有相关的(显式或可传递的)模块依赖项 类 和资源(来自 JAR 模块或 JMOD)都被编译成一个单一的优化的 JIMAGE 文件(同样,存储在 <jre>/lib/modules 下)。
    • JIMAGE 文件格式是模块化的,可以使用 CLI 工具创建、修改、反汇编或检查 jimage。例如。 jimage list $JAVA_HOME/lib/modules
    • JIMAGE 通常不应发布,而是与特定的自定义 JRE 版本一起发布;文件格式将来可能会发生变化。

物质:JMOD 的详细目的

一个新的,可选的Link时间阶段

如前所述,CLI 工具 jlink 在正常 Java 管道中引入了一个新的可选阶段 - link时相。此 link 时间阶段用于从一组 Java 9 个模块(带有 module-info.java 描述符的 JAR 或 JMOD)生成自定义构建的 JRE。

高阶阶段简述如下:

  • compile time(javac): 如javac documentation上所述,编译时阶段...

    ...reads class and interface definitions, written in the Java programming language, and compiles them into bytecode class files. It can also process annotations in Java source files and classes.

  • link time (jlink): 如'JEP 282: jlink: The Java Linker'所述,link 时间相位是...

    ...an optional phase between the phases of compile time (the javac command) and run-time (the java run-time launcher). Link time requires a linking tool that will assemble and optimize a set of modules and their transitive dependencies to create a run-time image or executable.

    Link time is an opportunity to do whole-world optimizations that are otherwise difficult at compile time or costly at run-time. An example would be to optimize a computation when all its inputs become constant (i.e., not unknown). A follow-up optimization would be to remove code that is no longer reachable.

  • 运行 time (java): 如上文所述javac documentation, 运行时间阶段...

    ...starts a Java application. It does this by starting the Java Runtime Environment (JRE), loading the specified class, and calling that class's main() method.

JMOD介绍

在link时间阶段,模块中的所有类和资源(有效的JAR模块或JMOD形式classes)都被编译成一个优化的JIMAGE 运行时间图像位于<jre>/lib/modules。未明确或传递包含的模块将 不会 包含在最终的 JIMAGE 中,从而节省大量 space。但是,在构建自定义 JRE 时,JRE 内部可能需要一些额外的文件;例如可执行命令或本机库。对于 JAR 模块,故事到此结束 - JAR 无法将文件(超出 JIMAGE 中包含的 类)毫无歧义地添加到内置的 JRE 中。

引入 JMOD:JMOD 能够将额外的文件添加到自定义构建的 JRE;一些示例(但不一定详尽无遗):可执行命令、配置文件、头文件、法律声明和许可证、本机库和手册页。这允许模块依赖性以其自己的方式塑造构建的 JRE。这些附加文件如何通过 CLI 工具插入到构建的 JRE 中的行为 jlink 将在下一节中记录。

JMOD 的目标是 编译时间和link 时间阶段,如'JEP 261: Module System':

中所述

JMOD files can be used at compile time and link time, but not at run time. To support them at run time would require, in general, that we be prepared to extract and link native-code libraries on-the-fly. This is feasible on most platforms, though it can be very tricky, and we have not seen many use cases that require this capability, so for simplicity we have chosen to limit the utility of JMOD files in this release.

新格式 - 不向后兼容 JAR

一个很好的问题可能是“为什么不启用 JAR 来添加 link-time 行为?”。这里有一个潜移默化的怀疑是,这不能为现有的 JAR 和工具提供足够的 backwards-compatibility 支持。 JAR 存档文件格式中没有保留文件名的规范。如果现有库在用于 link 时间的目录下存储任何资源,jlink 无法准确猜测它是要在 link 时间使用还是在 运行 需要时间。具有保留目录名称的新文件格式规范将解决此冲突问题 - 例如新的 JMOD 格式。使用 JMOD,对于 link 时间和 运行 时间指定的资源没有歧义。此外,还可以扩展 JMOD 格式以添加 nw 以后 JDK 版本中的功能,没有 backwards-compatibility 问题。

JMOD 文件格式类似于 JAR,因为它基于 ZIP 文件格式。 JMOD 文件具有以下保留目录名称,具有以下行为(这不一定是详尽的列表!):

  • bin (--cmds):复制到<jre>/bin
  • 的可执行命令
  • classes (--class-path): 用于包含到最终构建的 JIMAGE 中,存储在 <jre>/lib/modules
  • conf (--config): 附加配置复制到<jre>/conf;如果需要,可能用于控制任何捆绑模块的配置
  • include (--header-files):复制到 <jre>/include/ 的附加 C 头文件,用于使用 JNI 为 JVM 构建 C 库;例如在 java.base 中,JNI 接口被导出
  • legal (--legal-notices):复制到 <jre>/legal/<module name>/
  • 的模块的法律声明和许可证
  • lib (--libs): 复制到 <jre>/bin
  • 的原生库

For the curiously inclined, standard library JMODs (located under $JAVA_HOME/jmods in a JDK 9+) can be inspected with any application that reads ZIP archives.

主流支持...?

JMOD 没有被迅速采用并且文档可用性差的一个重要原因是,简而言之,它们对于绝大多数库和模块依赖项来说并不是必需的。虽然它们对特定用例仍然有用,但模块应该使用已经具有主流支持的 JAR 格式,因为它是在 1997 年用 JDK 1.1 定义的(module-info.java 模块支持添加了 JDK 2017 年 9 次)。

来自 CLI 工具的文档 jmod:

For most development tasks, including deploying modules on the module path or publishing them to a Maven repository, continue to package modules in modular JAR files. The jmod tool is intended for modules that have native libraries or other configuration files or for modules that you intend to link, with the jlink tool, to a runtime image.

一个意见:JMOD 可能至少在 非常 很长一段时间内不会被开发人员大量采用。大多数开发人员永远不会听说或知道 JMOD 的用途——他们也不需要。 JMOD 在幕后用于构建 JRE(所有 Java 标准库模块都是 JMOD)的关键目的,但不会影响绝大多数应用程序和项目,因为它们在 link 的利基用例时间。 Java 9 于 2017 年发布,Java 生态系统中的依赖项仍在努力可靠地拥有 module-info.class 描述符以使 JAR 成为有效的 fully-fledged 模块...

外卖

  • JMOD 是使用 CLI 工具创建 JRE 的一项基本新功能 jlink,可以使用其他文件自定义自定义构建的 JRE。
  • 部署 JAR 而不是 JMOD,除非特别需要 JMOD 的某些功能。 JAR 模块也与 jlink 兼容,因此没有必要发布仅包含 类 和资源的 JMOD。生态系统支持和工具不一定会很快采用 JMOD,并且在未来几年肯定会出现兼容性问题。
  • Java 生态系统这一领域的文档 确实 需要一些改进。

免责声明

在撰写此答案时,关于 Java 9 及更高版本的 JMOD 用途的文档很少。事实上,Google 搜索短语“java jmods”和“jmod format”带来​​了与第二个搜索结果完全相同的 Whosebug 问题。因此,有些方面可能解释不准确,但大体上是“方向正确”的;此外,它可能无法描绘出全貌。如果您发现任何问题或注意事项,请发表评论,我会尝试将其与此答案协调一致。