【问题标题】:Why did Java 9 introduce the JMOD file format?为什么 Java 9 引入了 JMOD 文件格式?
【发布时间】:2017-06-24 04:56:38
【问题描述】:

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

  • 罐子
  • JMOD
  • JIMAGE

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

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

问题:

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

【问题讨论】:

  • "JMOD 文件可以在编译时和链接时使用,但不能在运行时使用"。所以,我猜 JAR 对图书馆消费者来说仍然更有用,除非他们想要链接自己的运行时图像。
  • 据我所知,JMOD 文件仅用于构建自定义运行时映像(使用jlink)。这不是大多数人经常做的事情。它们甚至不是 Java SE 9 规范的一部分。目前,JMOD 文件只是 zip 文件,但将来可以更改。
  • @ZhekaKozlov Java 9 中不再有 jre/lib/rt.jar,因此当 javac 运行以查找 java.util.List 的代码时,它将从 $JDK_HOME/jmods 中获取/java.base.jmod 所以我认为如果你只关心 Java 9,你可以只分发 JMOD 文件而不是 jar 文件。 JMOD 文件不会在运行时使用 JVM 读取,因此如果您使用 jlink 制作自定义 jdk 映像以交付给客户,那么包含 JMOD 文件是没有意义的,Java 9 JRE 不附带 JDK 的 JMOD 文件。
  • @ams 抱歉,我没有收到您的最后评论。您是否暗示 Java 9 JDK 在运行时从 JMOD 文件中读取? AFAIK,那是错误的。您可以从 JDK 中删除整个 jmods 目录,而不会产生任何后果。此外,据我所知,Java 9 JDK 不会“拾取”您分发的自定义 JMOD。
  • @ams 据我所知(正如所有其他评论者在这里所说的那样),您将发布一个 JMOD 文件以使消费者能够jlink 他自己的运行时映像,其中包括您的 JMOD 模块.不过,我可能没有了解全部情况。

标签: java java-9 java-platform-module-system jmod


【解决方案1】:

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(在 Java 8 和之前的版本中替换 <jre>/lib/rt.jar)。 JMOD 的作用是在编译时和链接时,并且设计为在运行时使用。

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


长答案

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

拼图项目介绍

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

这个新模块系统的一个重要收获是 CLI 工具 jlink 使开发人员能够构建一个自定义 JRE,该 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 添加到类路径中。几乎每个库或依赖项,并且使用这种格式,所以在本节中略过。

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

    • 可以使用 CLI 工具 jmod 从 JAR 模块(即包含有效的 module-info.class)构建 JMOD。
    • 从 JDK 9 起,所有 Java 标准模块都存储在 JDK 安装中的 <jdk>/jmods 下。
    • 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 的单个优化 JIMAGE。此格式不是基于 ZIP 格式,它使用的自定义格式比原来的旧 JAR 格式更节省时间和空间,从而减少了启动时间。
    • 使用 CLI 工具 jlink 构建自定义 JRE 映像时,所有相关(显式或传递)模块依赖项的类和资源(来自 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 documentation所述,编译时间阶段...

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

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

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

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

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

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

JMODs介绍

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

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

JMOD 仅用于编译时间和链接时间阶段的,如“JEP 261: Module System”中所述:

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):复制到 <jre>/include/ 的附加 C 头文件,用于使用 JNI 为 JVM 构建 C 库;例如在java.base 中,JNI 接口被导出
  • legal (--legal-notices):复制到<jre>/legal/<module name>/ 的模块的法律声明和许可证
  • lib (--libs):复制到 <jre>/bin 的本机库

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

主流支持...?

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

来自CLI工具jmod的文档:

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

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

要点

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

免责声明

在撰写此答案时,关于 Java 9 及更高版本的 JMOD 用途的文档很少。事实上,Google 搜索短语“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。

    【讨论】:

    • 或者,换句话说:JMOD 文件不应该是 zip 文件,但由于资源不足,有一个众所周知的承诺会在以后提供更好的解决方案,这不会在未来十年内发生……
    • 原生代码曾经作为共享库发布,即平台原生形式。
    • 我的项目捆绑了some native libraries,而 lib/jar 依赖项只是与本机对象捆绑在一起,并找到一种在运行时加载它们的方法。由于recent events 和(现在缺少的)JavaFX 运行时以多种格式提供,其中一种是jmod,因此该项目正在从 JDK8 迁移到 JDK11。我很好奇这种格式是否有助于依赖/打包过程。
    猜你喜欢
    • 2017-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-11
    • 1970-01-01
    相关资源
    最近更新 更多