JRE 去哪了?一次讲清楚 JDK、JRE、JVM 到底是什么关系?

「Java 进阶之路」系列 Day15

写在前面

模块一并发编程系列写完了,从这篇开始进入模块二,回过头补一补 Java 最基础也最容易说不清楚的几个概念。第一个就是 JDK、JRE、JVM------这三个词几乎每个 Java 开发者天天挂在嘴边,但真要问"它们仨到底是什么关系",很多人只能含糊地说"JDK 比 JRE 大,JRE 比 JVM 大",再往下就说不清楚了。这篇把三者的边界、以及"为什么现在装了 JDK 却找不到独立的 JRE"这个容易让人困惑的现状一次讲透。


一、是什么:一层包含一层的关系

三者是严格的包含关系,从外到内依次是:

flowchart TB subgraph s1[JDK Java开发工具包] n1[开发工具 javac编译器 jar打包工具 javadoc文档生成器 jdb调试器] subgraph s2[JRE Java运行时环境] n2[核心类库 String ArrayList等基础类] subgraph s3[JVM Java虚拟机] n3[执行class字节码] end end end

一句话理解各自的定位:JVM 负责"跑" (执行字节码);JRE 负责"跑得起来" (JVM 加上程序运行必需的类库,让编译好的程序能实际运行);JDK 负责"写、编译、跑"全套(JRE 之外再加上开发时需要的编译器、调试器这些工具)。


二、为什么要分这三层:不同的人只需要不同的东西

这套分层设计对应的是三种不同的使用者角色:

  • 只想运行 Java 程序的普通用户:只需要 JRE,能把字节码跑起来就够了,不需要关心怎么把源码编译成字节码
  • 要开发 Java 程序的程序员 :需要完整的 JDK,因为要用 javac.java 源码编译成 .class 字节码,还要用调试器排查问题
  • 不管是谁,最终执行字节码的都是 JVM :这一层是三者里唯一真正需要为每个操作系统单独实现的部分,正是因为 JVM 把"跟操作系统打交道"这件事封装了起来,才实现了 Java 那句经典口号------"一次编译,到处运行"(Write Once, Run Anywhere):同一份 .class 字节码,扔到 Windows、Linux、macOS 上的 JVM 里都能跑,不需要针对每个平台重新编译源码。

这也是为什么三者要设计成一层包一层,而不是各自独立:JDK 开发出来的程序,最终一定是通过 JRE 提供的 JVM 去运行的,所以 JDK 里天然要包含一份完整的 JRE,才能保证"编译完立刻能在本机跑起来验证"。


三、怎么用:从源码到运行的完整链路

用一段最简单的代码走一遍完整流程:

flowchart LR A[Hello.java 源码] --> B[javac编译] B --> C[Hello.class 字节码] C --> D[java命令启动JVM] D --> E[加载并执行字节码] E --> F[控制台输出结果]
java 复制代码
// Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, JVM");
    }
}
bash 复制代码
# 用JDK提供的javac编译器,把.java源码编译成.class字节码
javac Hello.java
# 生成 Hello.class,这是一份跨平台的字节码文件,不是某个操作系统专属的可执行文件

# 用java命令启动JVM,加载并执行这份字节码
java Hello
# 输出:Hello, JVM

javac 是 JDK 独有的工具,JRE 里没有;而 java 这个启动 JVM 的命令,JRE 和 JDK 里都有------这也印证了前面"JRE 包含 JVM,JDK 包含 JRE"的层层关系。

一个容易让人困惑的现状:JRE 去哪了

如果你现在下载安装最新版 JDK,会发现安装目录里根本找不到一个独立叫 jre 的文件夹,只有 binlib 这些目录混在一起------这不是安装出了问题,而是从 JDK 9 引入模块化系统(Project Jigsaw)开始,官方就不再把 JDK 和 JRE 拆成两个独立的下载包分发了,JDK 11 之后更是彻底不单独提供 JRE 安装包。现在下载到的就是一个完整的 JDK,JRE 的能力已经内含在其中,不再是一个能单独感知到的物理文件夹。

这个变化背后还有一个更实用的产物:JDK 9 之后可以用 jlink 工具,根据你的程序实际用到的模块,定制打包出一个体积小得多的自定义运行时镜像,而不是笼统地打包一份包含所有类库的完整 JRE------这也是模块化之后官方索性不再单独维护"标准 JRE"这个产物的原因之一。

常见的坑:环境变量配置搞混

bash 复制代码
# 常见的环境变量配置(以类Unix系统为例)
export JAVA_HOME=/usr/lib/jvm/jdk-21
export PATH=$JAVA_HOME/bin:$PATH

JAVA_HOME 约定俗成指向 JDK 的安装目录(不是 JRE),很多工具链(比如 Maven、Gradle)都是靠这个环境变量去找 javac 来编译代码的。如果历史遗留代码或者旧文档让你把 JAVA_HOME 指向了某个单独的 JRE 目录,会导致编译阶段直接报错找不到 javac------这也是"JDK 包含 JRE,反过来不成立"这条包含关系在实际踩坑时最容易体现出来的地方。


四、面试追问

Q1:JDK、JRE、JVM 三者是什么关系?

是严格的包含关系:JDK 包含 JRE,JRE 包含 JVM。JVM 负责执行字节码;JRE 是 JVM 加上程序运行必需的核心类库,让编译好的程序能实际跑起来;JDK 是 JRE 之外再加上 javacjar、调试器这些开发工具,面向的是要编写和编译代码的开发者。

Q2:为什么说 JVM 是 Java 实现"一次编译到处运行"的关键?

因为 Java 源码编译出来的是统一格式的字节码(.class 文件),这份字节码本身不跟任何操作系统绑定;真正需要针对不同操作系统单独实现的,是负责加载和执行这份字节码的 JVM。不同平台各自实现自己的 JVM,但只要都遵循同一套字节码规范,同一份 .class 文件就能在任意装了对应 JVM 的平台上运行,不需要重新编译源码。

Q3:只想运行别人写好的 Java 程序,需要安装 JDK 吗?

理论上只需要 JRE 就够了,因为运行程序不需要 javac 这类开发工具,只需要 JVM 加上核心类库。不过实际情况是,JDK 9 之后官方已经不单独发布 JRE 安装包了,现在市面上能下载到的基本都是完整的 JDK,所以即使只是运行程序,通常也是装的 JDK,只是用不到里面的开发工具部分而已。

Q4:为什么 JDK 9 之后官方不再单独发布 JRE?

JDK 9 引入了模块化系统(Project Jigsaw),Java 类库被拆分成了一个个独立的模块。在这个基础上,官方提供了 jlink 工具,可以根据程序实际依赖的模块,按需定制打包出一个体积更小的自定义运行时镜像,而不再需要一份笼统打包了所有类库的"标准 JRE"。这让单独维护和发布一份通用 JRE 的意义大大降低,所以从 JDK 11 起就彻底不再提供独立的 JRE 下载包了。

Q5:JAVA_HOME 这个环境变量应该指向 JDK 目录还是 JRE 目录?

应该指向 JDK 的安装目录。这是因为编译工具链(比如 Maven、Gradle)以及很多依赖 Java 环境的工具,都是通过 JAVA_HOME/bin 去查找 javac 编译器的,而 javac 只存在于 JDK 里、不存在于单独的 JRE 里。如果误把 JAVA_HOME 指向了一个只有 JRE 的目录,编译阶段会直接报错找不到编译器。


下一篇预告

Day16 讲基本数据类型和类型转换里容易踩的坑------精度丢失是怎么发生的、自动拆装箱又藏着哪些看不见的性能陷阱和空指针风险。

相关推荐
JavaPub1 小时前
Ontology 本体论是什么?从哲学概念到 AI、知识图谱与软件工程
后端
PPPPickup1 小时前
基础知识差缺补漏-面试版持续更新
java·开发语言
liuxiaocheng1 小时前
聊聊 Vercel AI SDK 的流式协议:前后端到底是怎么"边想边说"的
前端·后端
深入云栈1 小时前
Nacos 2.x 源码深度解析 (二):通信协议迭代 —— HTTP长轮询到gRPC演进
java
得物技术1 小时前
得物知识问答:复合检索 Agent 的系统设计实践
人工智能·后端·ai编程
andongni2031 小时前
SpringBoot 入门实验报告
java·spring boot·后端
黄鸭部落格ShiYuYuanFang1 小时前
【分享】软考每日一练与解析-计组 8/12
java·开发语言
李广坤2 小时前
Agent 记忆系统详解:从"金鱼脑"到"过目不忘"的进化之路
后端
XWalnut2 小时前
LeetCode刷题 day37
java·数据结构·算法·leetcode