JVM体系结构
JVM 是一个运行 Java 字节码的虚拟计算机。它屏蔽了底层操作系统和硬件差异,使 Java 程序可以“一次编译,到处运行”。
# 1. JVM 在 Java 体系中的位置
Java 技术体系可以简化为:
Java源码 -> Java编译器 -> 字节码 -> JVM -> 操作系统 -> 硬件
开发者写的是 .java 文件,编译后得到 .class 文件。JVM 不直接运行 Java 源码,而是运行字节码。
# 2. JVM 的主要模块
JVM 主要由以下部分组成:
- 类加载子系统:负责加载
.class文件。 - 运行时数据区:负责管理运行时内存。
- 执行引擎:负责执行字节码。
- 本地方法接口:负责调用本地方法。
- 垃圾回收器:负责自动内存回收。
- 监控诊断能力:负责暴露运行时状态。
# 3. 类加载子系统
类加载子系统负责把 class 文件加载到 JVM 中,并完成验证、准备、解析、初始化等工作。
它解决的问题是:
- 去哪里找类?
- 类文件是否合法?
- 静态字段如何分配默认值?
- 符号引用如何解析为直接引用?
- 静态初始化代码什么时候执行?
类加载机制是理解框架、热部署、SPI、类冲突问题的基础。
# 4. 运行时数据区
JVM 执行程序时会划分多个内存区域:
- 程序计数器。
- Java 虚拟机栈。
- 本地方法栈。
- Java 堆。
- 方法区。
- 运行时常量池。
- 直接内存。
其中堆是垃圾回收管理的主要区域,栈和程序计数器是线程私有区域。
# 5. 执行引擎
执行引擎负责执行字节码。
主要方式包括:
- 解释执行:逐条解释字节码,启动快。
- JIT 编译:把热点代码编译为机器码,运行快。
- 本地方法调用:通过 JNI 调用 native 代码。
现代 HotSpot JVM 会结合解释执行和即时编译,让程序既能较快启动,又能在长期运行中获得更高性能。
# 6. 垃圾回收系统
Java 不要求开发者手动释放对象内存。JVM 会通过垃圾回收器识别不再使用的对象,并回收其占用的堆内存。
GC 需要解决三个问题:
- 哪些对象还活着?
- 如何回收垃圾对象?
- 如何减少停顿和性能损耗?
不同垃圾回收器会在吞吐量、延迟、内存占用、实现复杂度之间做不同取舍。
# 7. 监控与诊断
JVM 提供了丰富的诊断能力:
- GC 日志。
- 线程转储。
- 堆转储。
- JFR 事件记录。
- JMX 监控。
jcmd、jstack、jmap、jstat等命令行工具。
这些工具是线上排障的基本装备。
# 8. JVM 运行时协作流程
一个对象从代码到运行,大致会经过下面的协作过程:
1. 编译阶段
Java 源码 -> Class 文件
2. 类加载阶段
ClassLoader 读取字节码
-> 验证 class 合法性
-> 准备静态变量默认值
-> 解析符号引用
-> 执行类初始化
3. 执行阶段
解释器先解释执行
-> 热点代码触发 JIT 编译
-> 编译成本地机器码
4. 内存阶段
对象主要分配到堆
方法调用创建栈帧
类元数据进入元空间
5. 回收与诊断阶段
GC 回收不可达对象
通过日志、dump、JFR 观察系统
可以把 JVM 想成一个流水线系统,而不是一个黑盒:
| 阶段 | 核心问题 | 常见故障 |
|---|---|---|
| 编译 | 生成的字节码是否兼容运行时 JDK | UnsupportedClassVersionError |
| 类加载 | 类能不能被正确找到和初始化 | ClassNotFoundException、NoClassDefFoundError |
| 内存分配 | 对象、类元数据、线程栈是否够用 | 各类 OOM |
| 执行优化 | 热点代码是否被正确优化 | 启动慢、预热慢、CPU 高 |
| GC | 对象是否能及时回收 | Full GC、延迟抖动 |
| 诊断 | 是否有足够证据定位问题 | 无 dump、无 GC 日志 |
# 9. JVM 规范和 HotSpot 的区别
学习 JVM 时要区分两个层面:
- JVM 规范:规定 Java 虚拟机应该有什么语义,比如运行时数据区、类加载过程、字节码指令。
- 具体实现:例如 HotSpot、OpenJ9,它们可以用不同方式实现规范。
例如:
| 概念 | 规范层面 | HotSpot 实现层面 |
|---|---|---|
| 方法区 | JVM 规范定义的运行时区域 | JDK 8 后主要用元空间实现 |
| 垃圾回收 | 规范不强制具体算法 | HotSpot 提供 G1、ZGC 等 |
| JIT | 规范不要求必须存在 | HotSpot 有 C1/C2 分层编译 |
| 对象布局 | 规范不规定具体布局 | HotSpot 有对象头、Klass Pointer 等 |
很多面试题会混用“规范”和“实现”。回答时最好补一句“以 HotSpot 为例”,这样更严谨。
# 10. 开发中常见误区
误区一:JVM 内存就是堆内存。
实际上 JVM 进程还包含元空间、直接内存、线程栈、Code Cache、native 内存等。误区二:有 GC 就不会内存泄漏。
GC 只能回收不可达对象。对象如果被错误引用链持有,仍然不会被回收。误区三:JIT 一定让所有代码变快。
JIT 主要优化热点代码。短生命周期程序可能还没充分预热就结束了。误区四:默认参数一定适合所有服务。
默认值适合通用场景,但容器、低延迟、大堆、高吞吐服务都需要结合业务目标评估。
# 11. Tips 快问快答
Q:JVM 和 JRE、JDK 是什么关系?
A:JDK 包含开发工具和运行环境,JRE 包含运行 Java 程序所需组件,JVM 是执行字节码的虚拟机核心。现代 JDK 发布形态已经弱化了独立 JRE 的概念,但层次关系仍有助于理解。
Q:JVM 是否只支持 Java 语言?
A:不是。JVM 执行的是字节码,Kotlin、Scala、Groovy 等语言都可以编译成 JVM 字节码运行。
Q:为什么 Java 能跨平台?
A:因为 Java 编译生成平台无关字节码,不同平台由对应 JVM 把字节码执行为本地机器行为。
Q:学习 JVM 最重要的主线是什么?
A:类如何加载、对象如何分配、方法如何执行、垃圾如何回收、问题如何诊断。
# 12. 总结
JVM 体系结构可以概括为:类加载把字节码带进 JVM,运行时数据区承载程序状态,执行引擎执行代码,垃圾回收器管理内存,诊断工具帮助观察运行状态。后续所有 JVM 知识都可以挂在这张地图上。