为什么Java 9引入了JMOD文件格式?简答题长答案免責聲明

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

  • 杰莫德
  • 吉美琪

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

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

问题:

  • 为什么Java 9引入了JMOD文件格式?
  • 库作者应该分发 JMOD 文件还是 JAR 文件,或者同时分发两者?
  • jmod文件应该发布到maven存储库吗?

答案 1

JMODs的目的没有得到很好的记录,现有的文档相当稀少。以下是从我的理解中对系统的深入解释。

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


简答题

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

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

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


长答案

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

拼图项目介绍

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

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

例如,使用 ,由 JDK 15 构建的极简主义 JRE,只有模块的大小约为 40MB,与 JDK 15 的 ~310MB 大小明显并列。这对于发布最小的自定义 JRE 特别有用,例如对于精益 Docker 映像。新的模块系统为Java生态系统带来了巨大的好处,这些好处已经在别处进行了详细讨论,因此在这里没有进一步详细说明。jlinkjava.base

3 个 J:JAR、JMOD 和 JIMAGES

对 JAR、JMOD 和 JIMAGE 的高级描述并不能很快地解释这三种文件格式的角色。以下是对每种方法用途的非详尽概述:

  • 罐子:基于 ZIP 文件格式的经典格式,用于在运行时将类和资源捆绑到类路径中。这是自1997年JDK 1.1以来制定的事实上的主流标准。JAR 可以使用 / 标志添加到类路径中。几乎每个库或依赖项都具有现在将来都使用此格式,因此在本节中将其掩盖。java-cp-classpath

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

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

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

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

实质内容:JMOD的详细目的

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

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

高级阶段简要描述如下:

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

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

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

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

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

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

    ...启动一个 Java 应用程序。它通过启动 Java 运行时环境 (JRE)、加载指定的类并调用该类的 main() 方法来实现此目的。

JMOD的介绍

在链接时间阶段,模块中的所有类和资源(有效的 JAR 模块或表单 JMOD)都编译成位于 的单个优化的 JIMAGE 运行时映像。未明确或未传递包含的模块将不会包含在此最终 JIMAGE 中,从而节省大量空间。但是,在构建自定义 JRE 时,JRE 内部可能需要一些其他文件。例如,可执行命令或本机库。对于 JAR 模块,故事到此为止 - 对于 JAR 来说,没有办法将文件(除了 JIMAGE 中包含的类之外)添加到构建的 JRE 中而不会产生歧义。classes<jre>/lib/modules

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

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

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

新格式 - 与 JAR 没有向后兼容性

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

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

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

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

主流支持...?

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

从 CLI 工具 jmod 的文档:

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

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

要点

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

免責聲明

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


答案 2

以下是来自 JEP 261: Module System 的一些引用,其中包含有关 JMOD 文件的一节。

为什么?

来自 JEP 261

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

JMOD文件的最终格式是一个悬而未决的问题,但现在它基于ZIP文件。

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

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

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

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


推荐