为什么Java 9引入了JMOD文件格式?

ams*_*ams 33 java java-platform-module-system java-9

Java 9有三种方法可以将编译后的代码打包到文件中:

  • JMOD
  • JIMAGE

JIMAGE针对速度和空间进行了优化,并在运行时由JVM使用,因此有理由引入JIMAGE.JIMAGE文件不应发布到maven repos或在编译或链接时使用.

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

问题:

  • 为什么Java 9引入了JMOD文件格式?
  • 库作者是应该分发JMOD文件还是JAR文件?
  • jmod文件应该发布到maven repos吗?

con*_*ion 33

JMOD 的用途没有很好的文档记录,现有文档相当稀少。这是对系统的深入解释,根据我的理解。

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


简短的(更)答案

Java 9 的新模块系统Project Jigsaw引入了新的可选链接时间阶段的概念,当使用 CLI 工具jlink构建自定义空间优化的 JRE时会发生这种情况。jlink将所有显式/可传递 JAR 模块/JMOD 依赖项捆绑到一个缩小的 JRE 中;依赖图中的所有其他无法访问的依赖项(从指定的根模块开始)都不会捆绑到构建的 JRE 中。从 JDK 9+ 开始,Java 的所有标准库都已分解为 JMOD,位于<jdk>/jmods.

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

对于一般的库/应用程序,只需要构建和推送 JAR,而不是 JMOD;只有在特定条件下,JMOD 才会提供链接时间阶段所需的关键功能。在撰写本文时,Maven 似乎没有为 alpha 版本插件之外的 JMOD 提供强大的支持org.apache.maven.plugins:maven-jmod-plugin


长答案

这个冗长的答案动机更复杂,并揭示了新模块系统的基本运作方式。整篇文章都非常强调 CLI 工具jlink,因为 JMOD 是专门为该工具引入的这个新的可选链接时间阶段而设计的。

拼图项目介绍

Java 9在“ JEP 261:模块系统”中引入了Project Jigsaw,这是一种新颖的模块系统,可用于最小化启动时间和 JRE 的大小。作为此版本的一部分,CLI 实用程序、和以及 JMODs/ s(基于 ZIP)和 JIMAGEs/ s 的新文件格式一起被引入。jmodjimagejlink.jmod.jimage

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

举一个使用 优势的例子jlink,一个从 JDK 15 构建的极简 JRE 的java.base大小约为 40MB,与 JDK 15 的约 310MB 大小形成鲜明对比。这对于传送最小的自定义 JRE 尤其有用,例如用于精益 Docker 映像。新的模块系统为 Java 生态系统带来了显着的好处,这些好处已经在别处详细讨论过,因此这里不再详细阐述。

3 个 J:JAR、JMOD 和 JIMAGE

JARs、JMODs 和 JIMAGEs 的高级描述并不能很快提供一种强烈区分三种文件格式角色的解释。以下是对每个目的的非详尽概述:

  • JAR:基于 ZIP 文件格式的经典格式,用于在运行时将类和资源捆绑到类路径中。这是自 1997 年 JDK 1.1 以来制定的事实上的主流标准。可以使用java -cp/-classpath标志将 JAR 添加到类路径中。几乎每一个图书馆或依赖,和要使用这种格式,所以在这一部分掩盖了。

  • JMOD:一种基于 ZIP 文件格式的新格式,用于捆绑 JAR 可以包含的相同内容,但支持在可选链接时使用的其他文件(例如可执行文件、本机库、配置、合法许可证等)构建自定义 JRE 时的阶段。JMOD 旨在在编译时和链接时使用,但不能在运行时使用。很可能引入了这种新格式(而不是扩展 JAR),因为这种新的基于归档格式的目录具有特殊含义,该格式与已经使用相同目录名称的 JAR向后兼容。

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

注意:可能有一种方法可以在运行时轻松地将 JMOD 添加到类路径中;然而,研究并未明确说明与此相关的任何功能。仅向类路径添加 JMOD 不足以使用类和资源。ClassLoader但是,可以使用自定义来在运行时正确解析 JMOD 存档中的类和资源文件;这通常是不推荐的,也不是 JMOD 的目的。

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

实质:JMOD 的详细目的

一个新的、可选的链接时间阶段

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

高级阶段简要描述如下:

  • 编译时间javac):如javac文档所述,编译时间阶段...

    ...读取用 Java 编程语言编写的类和接口定义,并将它们编译成字节码类文件。它还可以处理 Java 源文件和类中的注释。

  • 链接时间( jlink):如“ JEP 282: jlink: The Java Linker ”中所述,链接时间阶段是...

    ...编译时(javac 命令)和运行时(java 运行时启动器)阶段之间的可选阶段。链接时需要一个链接工具,该工具将组装和优化一组模块及其传递依赖项,以创建运行时映像或可执行文件。

    链接时间是进行全局优化的机会,否则在编译时很难或在运行时代价高昂。一个例子是当所有输入变为常数(即,不是未知的)时优化计算。后续优化将删除不再可访问的代码。

  • 运行时java):如javac文档中所述,运行时阶段...

    ...启动 Java 应用程序。它通过启动 Java 运行时环境 (JRE)、加载指定的类并调用该类的 main() 方法来完成此操作。

JMOD 介绍

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

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

JMOD用于编译时间和链接时间阶段,如“ JEP 261:模块系统”中所述:

JMOD 文件可以在编译时和链接时使用,但不能在运行时使用。要在运行时支持它们,通常需要我们准备好即时提取和链接本机代码库。这在大多数平台上都是可行的,尽管它可能非常棘手,而且我们还没有看到很多需要此功能的用例,因此为简单起见,我们选择在此版本中限制 JMOD 文件的实用性。

新格式 - 不向后兼容 JAR

一个很好的问题可能是“为什么不启用 JAR 来添加链接时行为?”。这里有一个偷偷摸摸的怀疑,这不能为现有的 JAR 和工具提供足够的向后兼容性支持。JAR 归档文件格式中没有保留文件名的规范。如果现有库在用于链接时的目录下存储任何资源,jlink无法准确猜测它是在链接时消耗还是在运行时需要。具有保留目录名称的新文件格式规范将解决此冲突问题 - 例如新的 JMOD 格式。使用 JMOD,对于链接时间和运行时间指定哪些资源没有歧义。此外,还可以扩展 JMOD 格式以在更高版本的 JDK 中添加新功能,而不会出现向后兼容性问题。

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

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

对于奇怪的倾向,$JAVA_HOME/jmods可以使用任何读取 ZIP 档案的应用程序检查标准库 JMOD(位于JDK 9+ 下)。

主流支持...?

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

从 CLI 工具的文档中jmod

对于大多数开发任务,包括在模块路径上部署模块或将它们发布到 Maven 存储库,请继续将模块打包到模块化 JAR 文件中。jmod 工具适用于具有本机库或其他配置文件的模块,或者您打算使用 jlink 工具链接到运行时映像的模块。

观点:至少在长一段时间内,JMOD 可能不会被开发人员大量采用。大多数开发人员永远不会听到或知道 JMOD 的用途——他们也不需要。JMOD 在构建 JRE 的幕后发挥着重要作用(所有 Java 标准库模块都是 JMOD),但由于它们在链接时的特殊用例,因此不会影响绝大多数应用程序和项目。Java 9 于 2017 年发布,Java 生态系统中的依赖项仍然难以可靠地拥有一个module-info.class描述符来使 JAR 成为有效的成熟模块......

外卖

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

免责声明

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


Mic*_*ter 10

以下是JEP 261:模块系统的一些引用,其中包含有关JMOD文件的部分.

为什么?

JEP 261:

新的JMOD格式超出了JAR文件,包括本机代码,配置文件和其他类型的数据,这些数据自然不适合JAR文件(如果有的话).

JMOD文件的最终格式是一个开放的问题,但现在它基于ZIP文件.

开发人员应该发布JMOD文件吗?

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

JMOD文件可以在编译时和链接时使用,但不能在运行时使用.

(说实话,我不确定在JDK 9之前如何发布本机代码.)对于绝大多数开发人员(没有本机库或其他角落案例),我们只会发布模块化jar.

  • 或者,换句话说:JMOD文件不应该是zip文件,但是由于资源不足,存在着这种kludge,众所周知的承诺将在以后提供更好的解决方案,这在未来十年内不会发生... (5认同)