当前位置:首页 > 文章列表 > 文章 > java教程 > Java jlink 运行时镜像为什么不包含完整JDK

Java jlink 运行时镜像为什么不包含完整JDK

来源:17golang原创 2026-10-31 02:12:54 0浏览 收藏

用 jlink 生成的运行时镜像没有完整 JDK,是因为它的目标从来不是复制 JDK,而是根据指定的根模块和传递依赖组装一个“够应用运行”的自定义运行时。javac、javadoc、头文件和部分调试资源属于开发或诊断工具,不会因为它们存在于原始 JDK 中就自动进入镜像。

官方地址:https://docs.oracle.com/en/java/javase/25/docs/specs/man/jlink.html

判断 jlink 结果时,先看应用的模块依赖闭包,再看是否显式添加了额外模块;不要用“镜像里没有 javac”反推 jlink 生成失败。

先区分 JDK、运行时镜像和应用模块

完整 JDK 是开发工具集合,除了运行 Java 程序需要的模块,还包含编译、打包、诊断和文档工具。jlink 面向的是部署阶段:它从模块路径中找到应用模块,把根模块以及依赖图中可达的模块放入输出目录。

因此,镜像里常见的是 java 启动器和应用需要的标准模块,例如 java.base、java.logging;而 javac、javadoc、jar 等开发命令并不是普通应用运行的必需依赖。缺少它们,通常只是说明镜像边界生效了。

应用模块、根模块、传递依赖与开发工具之间的静态边界关系说明图
图1:应用模块通过根模块和传递依赖形成运行时闭包,开发工具位于默认输出边界之外;这是静态结构说明图,不是运行截图。

jlink 为什么不会自动复制完整 JDK

--add-modules 指定的是根模块,jlink 再根据模块声明解析传递依赖。它不是“从 JDK 目录里挑文件”,也不是“把所有可观察模块都打包”。官方命令说明明确指出,输入模块可以来自模块化 JAR、JMOD 或 exploded module,输出内容由指定模块及其传递依赖构成。

例如,应用模块声明只依赖日志能力时,可以这样构建一个运行时镜像:

# 只把应用模块和它声明的运行时依赖放进镜像
jlink \
  --module-path mods:$JAVA_HOME/jmods \
  --add-modules com.example.app \
  --strip-debug \
  --no-man-pages \
  --no-header-files \
  --output runtime

# 运行时镜像的启动器位于输出目录的 bin 下
./runtime/bin/java --module com.example.app/com.example.app.Main

这里的关键不是参数数量,而是根模块 com.example.app。如果它的 module-info.java 只写了 requires java.logging,那么 java.logging 会被解析;没有被依赖图触及的开发模块则不会因为“同属一个 JDK”而加入。

先用两个命令判断到底缺了什么

遇到镜像启动失败时,我会先分别确认镜像里有什么,以及应用声明了什么。第一个命令观察输出镜像的模块列表,第二个命令从应用或依赖 JAR 推导模块依赖:

# 查看镜像实际包含的模块,避免凭目录名称猜测
./runtime/bin/java --list-modules

# 从模块化 JAR 推导需要的 JDK 模块;路径替换成自己的应用文件
jdeps --print-module-deps --ignore-missing-deps app.jar

如果 jdeps 给出的依赖包含 java.desktop,但构建命令的根模块没有把它带入依赖闭包,就应该显式加入或修正模块声明。如果问题是找不到 javac,则要先确认部署包是否真的需要编译;一个只负责运行已编译程序的镜像不应把编译器当作默认依赖。

需要额外能力时,显式添加模块

“不包含完整 JDK”不等于“不能使用额外能力”。图形界面、区域设置、命名服务等能力都可以按应用场景加入。做法是把真正需要的模块写在 --add-modules 中,或让应用模块的 requires 关系表达它们。

# GUI 应用显式加入桌面模块,并保留中文与英文等区域设置
jlink \
  --module-path mods:$JAVA_HOME/jmods \
  --add-modules com.example.desktop,jdk.localedata \
  --include-locales=zh-CN,en \
  --strip-debug \
  --output desktop-runtime

# 只保留需要的压缩级别;2 表示 ZIP 资源压缩
jlink \
  --module-path mods:$JAVA_HOME/jmods \
  --add-modules com.example.app,java.logging \
  --compress=2 \
  --output compact-runtime

--include-locales 使用时要同时加入 jdk.localedata。这类参数体现的是“按需加能力”,不是恢复完整 JDK;每加入一个模块,都应能在应用功能或服务发现需求中找到理由。

必需依赖、按需加入模块、服务提供者与运行时镜像之间的静态关系说明图
图2:按需模块和服务绑定都需要显式配置,最终仍然汇入面向部署的运行时镜像;这是结构示意图,不是软件截图。

服务发现为什么还可能少模块

如果应用通过服务加载机制使用实现,而不是直接在模块声明中引用实现模块,仅列出应用模块有时还不够。jlink 提供 --bind-services,用于把服务提供者模块及其依赖纳入链接范围。

# 应用使用 ServiceLoader 时,按需绑定可观察的服务提供者
jlink \
  --module-path mods:$JAVA_HOME/jmods \
  --add-modules com.example.app \
  --bind-services \
  --output service-runtime

这仍然不是“把所有 JDK 服务都塞进来”。它只改变服务绑定这一类解析行为,镜像依旧围绕应用模块、可观察模块和依赖闭包生成。JDK 25 的 jpackage 默认也不再自动包含服务绑定;如果包装应用确实需要它,应通过 jlink 选项明确传入。

把镜像构建当作可升级的交付物

自定义运行时不是一次生成、永久不变的压缩包。官方文档把更新责任交给开发者,所以构建脚本应固定 JDK 工具链、记录根模块,并在依赖变化后重新生成镜像。

现象优先判断处理方式
镜像没有 javac是否只是部署运行通常保留最小运行时,不补完整 JDK
启动时报缺少标准模块模块依赖闭包是否遗漏用 jdeps 和 --list-modules 对照后显式加入
ServiceLoader 找不到实现是否需要服务绑定评估 --bind-services 或明确加入提供者模块
Locale 结果不完整是否缺少 jdk.localedata加入模块并设置 --include-locales

我的经验是把“缺少完整 JDK”拆成两个问题:应用运行是否缺少模块,以及团队是否误把开发工具当成运行时依赖。前者通过模块依赖和命令检查解决,后者通过重新确认交付边界解决。

相关问题

jlink 生成的镜像能不能继续编译 Java 代码?

默认不能把它当作完整开发环境。镜像的目标是运行应用;需要编译时应使用单独的 JDK,或明确评估是否真的要把开发工具放入交付环境。

为什么加了 java.desktop 仍然启动失败?

还要检查应用自己的模块是否在模块路径中、依赖是否可解析,以及是否存在其他缺少的模块。不要只根据一个报错模块停止判断。

是否应该直接使用完整 JDK 避免排查?

开发机和排障环境可以使用完整 JDK,但生产分发通常更适合按应用依赖生成镜像;这样边界清楚,升级也可以在构建流程中重复完成。

参考文档:https://docs.oracle.com/en/java/javase/25/docs/specs/man/jlink.html

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
PHP grapheme_strpos 的偏移量为什么不按字节计算PHP grapheme_strpos 的偏移量为什么不按字节计算
上一篇
PHP grapheme_strpos 的偏移量为什么不按字节计算
Go go-shp 怎样遍历Shapefile中的几何记录
下一篇
Go go-shp 怎样遍历Shapefile中的几何记录
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    801次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    792次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    879次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    849次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    680次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码