73

Java 9 提供三种方式将编译后的代码打包到文件中:

  • JMOD
  • 图像

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

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

问题:

  • 为什么 Java 9 引入了 JMOD 文件格式?
  • 库作者应该分发 JMOD 文件还是 JAR 文件或两者兼而有之?
  • jmod 文件是否应该发布到 maven 存储库?
4

2 回答 2

49

JMODs 的目的没有很好的记录,现有的文档也很少。根据我的理解,这是对系统的深入解释。

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


简短(呃)答案

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 和之前的版本中替换)。JMODs 的作用是在编译时和链接时,而不是设计为在运行时使用。

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


长答案

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

拼图项目介绍

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

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

jlink举一个使用java.base. 这对于发布最小的自定义 JRE(例如精简 Docker 映像)特别有用。新的模块系统为 Java 生态系统带来了显着的好处,这些好处已经在别处详细讨论过,因此这里不再详细阐述。

3 J:JAR、JMOD 和 JIMAGE

JARs、JMODs 和 JIMAGEs 的高级描述并不能很快解释这三种文件格式的作用。以下是每个目的的非详尽概述:

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

  • JMODs:一种基于 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:模块化运行时映像”中引入的一种特殊文件格式,它是一个运行时映像,包含 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:Java Linker '中所述,链接时间阶段是......

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

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

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

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

JMODs的介绍

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

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

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 中,存储在<jre>/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

出于好奇,标准库 JMOD(位于$JAVA_HOME/jmodsJDK 9+ 中)可以使用任何读取 ZIP 存档的应用程序进行检查。

主流支持...?

JMOD 没有被迅速采用并且文档可用性较差的一个重要原因是,很简单地说,它们对于绝大多数库和模块依赖项来说不是必需的。虽然它们对于特定用例仍然有用,但模块应该使用自 1997 年使用 JDK 1.1 定义以来已经获得主流支持的 JAR 格式(module-info.java2017 年 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 问题。因此,有些方面可能解释不准确,但总体上是“方向正确的”;此外,它可能无法描绘出全貌。如果您发现任何问题或警告,请发表评论,我将尝试将其与此答案相协调。

于 2020-10-05T05:02:53.157 回答
14

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

为什么?

来自JEP 261

新的 JMOD 格式超越了 JAR 文件,将本机代码、配置文件和其他类型的数据包含在 JAR 文件中(如果有的话)。

JMOD 文件的最终格式是一个开放的问题,但目前它基于 ZIP 文件。

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

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

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

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

于 2017-06-24T12:00:06.500 回答