首页 / Java 入门教程 / JDK、JRE 与 JVM 的关系

Java 入门教程

JDK、JRE 与 JVM 的关系

本教程共 100 篇 · 第 3 篇 · 更新于 2026-08-05 · 约 5 分钟阅读

JavaJava 入门教程JDKJREJVM字节码

本节目标:分清 JDK、JRE、JVM 三个缩写各自负责什么,理解字节码怎么被执行,装环境时知道该装哪个。

三个缩写,一层套一层

初学者最容易被这三个词绕晕。先记全称:

  • JVM = Java Virtual Machine,Java 虚拟机
  • JRE = Java Runtime Environment,Java 运行时环境
  • JDK = Java Development Kit,Java 开发工具包

它们不是三个并列的东西,而是一层套一层的包含关系:

 ┌─   ┌────────────────────────────────────┐
 │    │  javac、jar、javadoc、jshell ...   │  ← 开发工具
 │    └────────────────────────────────────┘
JDK ┌─┌────────────────────────────────────┐
 │  │ │                                    │
 │ JRE│      JVM  +  标准类库              │  ← 运行所需
 │  │ │                                    │
 └─ └─└────────────────────────────────────┘
      ┌───────┐┌───────┐┌───────┐┌───────┐
      │Windows││ Linux ││ macOS ││others │  ← 操作系统
      └───────┘└───────┘└───────┘└───────┘

一句话版本:JDK 包含 JRE,JRE 包含 JVM

要写 Java,装 JDK 就够了,它把另外两个都带上了。

JVM:真正干活的那一层

JVM 是这套体系的核心。它是一个软件模拟出来的「计算机」,有自己的指令集、自己的内存布局。

它干四件事:

  1. 加载:把 .class 文件读进内存。
  2. 校验:检查字节码是否合法,防止恶意代码搞破坏。
  3. 执行:把字节码指令翻译成当前 CPU 能懂的机器码并运行。
  4. 管内存:分配对象、回收垃圾(GC),不用你手动释放。

如 §01 所述,JVM 像翻译官——字节码是「世界语」,JVM 负责讲成当地话。

关键在于:Windows、Linux、macOS 各有各的 JVM,但它们读的是同一份字节码。跨平台就是这么实现的。

Note

严格说 JVM 是一份「规范」,不是一个具体软件。Oracle HotSpot、OpenJ9、GraalVM 都是它的实现。日常说 JVM 一般指 HotSpot。

字节码长什么样

光说不练没感觉。写个最短的程序看看:

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello");
    }
}

编译它,然后用 JDK 自带的 javap 反汇编:

javac Hello.java
javap -c Hello.class

你会看到类似这样的输出:

Compiled from "Hello.java"
public class Hello {
  public Hello();
    Code:
       0: aload_0
       1: invokespecial #1     // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #7     // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #13    // String Hello
       5: invokevirtual #15    // Method java/io/PrintStream.println:(...)V
       8: return
}

getstaticldcinvokevirtual 就是字节码指令,长得像汇编但不属于任何真实 CPU。

它们只有 JVM 认识。这就是「字节码是 JVM 的机器语言」这句话的含义。

Tip

#7#13 这些编号指向常量池,不同 JDK 版本编出来的数字会不一样,不用对着抄。看懂指令名就够了。

解释执行 + JIT:Java 为什么不慢

早期 Java 被吐槽慢,因为 JVM 一条条解释字节码,比机器码慢一大截。

现在的 JVM 加了 JIT(Just-In-Time)编译器。它会盯着程序运行,发现某段代码被反复执行(比如循环里的方法),就把它直接编译成本地机器码缓存起来。

下次再跑到这段代码,走的是机器码,速度接近 C。

所以现代 Java 的性能表现是:启动稍慢,跑起来很快。长期运行的服务端程序正好吃这个红利。

JRE:只想跑,不想写

JRE = JVM + 标准类库。

标准类库就是 StringArrayListMath 这些你即将天天用到的现成类。它们已经编译成字节码,打包在 JDK 里。

只装 JRE 的机器可以运行 Java 程序,但没有编译器,写不了代码。

Warning

从 Java 11 开始,Oracle 不再单独发布 JRE 安装包。现在统一装 JDK;如果只想给用户一个精简运行时,用 jlink 工具自己裁剪一个。老教程里「先装 JRE 再装 JDK」的说法已经过时了。

JDK:开发者要的那一套

JDK 在 JRE 之上补齐了开发工具。装完 JDK,在它的 bin 目录下能找到一堆可执行文件:

命令作用
java启动 JVM,运行程序
javac编译器,.java.class
jar把一堆 .class 打包成 .jar
javadoc从文档注释生成 HTML API 文档
javap反汇编,查看字节码
jshell交互式命令行(Java 9 引入),随手试语法
jdb命令行调试器
jlink裁剪定制运行时镜像

初学阶段用得最多的是 javacjavajshell 也值得记住,试一小段语法不用建文件。

class 文件版本号:高版本编的跑不了低版本

每个 .class 文件里都记着一个版本号,标明它是哪个 JDK 编出来的。

Java 版本class 文件版本
Java 852
Java 1155
Java 1761
Java 2165
Java 2569

规则很简单:高版本 JVM 能跑低版本字节码,反过来不行

用 Java 25 LTS 编译的类,扔到 Java 17 的环境里运行,会直接抛:

java.lang.UnsupportedClassVersionError: Hello has been compiled by a more
recent version of the Java Runtime (class file version 69.0), this version
of the Java Runtime only recognizes class file versions up to 61.0

这个错误初学者踩得非常多,通常是本机 JDK 和服务器 JDK 版本不一致造成的。看到 UnsupportedClassVersionError,第一反应就该是查版本。

一个常被忽略的事实:JVM 不只跑 Java

JVM 认的是字节码,它并不关心这份字节码是从哪种语言编译来的。

于是出现了一批「JVM 语言」:

语言特点常见场景
Kotlin语法简洁,空安全Android、后端
Scala函数式特性强大数据(Spark)
Groovy动态类型,脚本友好Gradle 构建脚本
ClojureLisp 方言特定领域

它们编译出的 .class 文件和 Java 编译出的没有本质区别,能互相调用。

这也解释了为什么第 2 章说「学 Java 转 Kotlin 成本很低」——底层跑的是同一套东西。

反过来看,把 JVM 吃透的收益不止于一门语言。类加载、内存模型、GC 这些知识,换到 Kotlin、Scala 上照样成立。

完整流程串一遍

把上面的内容连起来,一个 Java 程序的一生是这样:

你写代码          Hello.java        (纯文本)
   │ javac 编译

class 文件        Hello.class       (版本号 69)
   │ java 启动 JVM

JVM 加载 → 校验 → 解释执行 / JIT 编译 → 输出结果

javac 属于 JDK,java 启动的是 JVM,运行时用到的 String 等类来自 JRE 的标准类库。

三者各司其职,缺一环程序就跑不起来。

小结

JDK 套着 JRE,JRE 套着 JVM,开发只需要装 JDK。

JVM 负责加载、校验、执行字节码和管理内存,是跨平台的实现者;JIT 让长期运行的程序性能接近本地代码。

JRE 是 JVM 加标准类库,Java 11 起不再单独发布。

class 文件带版本号,高版本 JVM 兼容低版本字节码,反向会报 UnsupportedClassVersionError