这段时间帮组里的新人做晋升辅导,发现一个特别普遍的问题:JVM的基础概念大家都能说个大概,但一往深处追问就会卡壳。比如都知道堆是放对象的,但问一句"所有对象一定在堆上分配吗?"就开始含糊;知道有垃圾回收,但问"Full GC的时候其他线程在干什么"又答不上来。这让我也决定把JVM完整地重新过一遍,于是便有了这篇复习笔记。
这篇笔记计划分上下两篇。上篇围绕运行时数据区域、类加载机制、对象分配三条主线展开,把JVM最底层的"骨架"搭清楚;下篇再安排垃圾回收、故障排查与调优实践。内容除了概念本身,更多的是一些容易在面试中暴露出来、平时写代码又很难察觉的细节。如果你正在准备JVM相关的面试,或者刚入职不久想系统建立对JVM的认知,这篇笔记应该能帮你省不少事。
需要提前说明的是,文章中涉及的JVM参数、默认行为基本都是围绕较常见的HotSpot虚拟机、JDK 8及以上版本来讲的。版本不同会有细节差异,这一点在复习时也要留个心眼。
1. JVM复习总纲:先搞清楚它到底解决了什么问题
1.1 跨平台只是最表面的答案
很多人一上来就说JVM的价值是实现"一次编译,到处运行",这话没错,但远不够。跨平台只是JVM附带的一个结果,它真正做的事,是构筑了一道从操作系统到Java代码之间的"中间层"。
你看C/C++写的程序,编译产物是机器码,直接跟CPU架构、操作系统绑定。而Java程序编译出来的是字节码,字节码不面向任何具体的CPU,它面向的是JVM这套抽象的指令集规范。真正把字节码翻译成当前机器能执行的指令的人,是JVM的即时编译器(JIT)和解释器。
这道中间层带来的好处不仅仅是跨平台,还在于它把所有和操作系统打交道的脏活累活都收拢到了一起:内存管理、线程调度、锁的适配、文件IO的系统调用封装……这些事全部交给JVM去适配当前平台,写Java的人面对的是一个统一的抽象。这也是JVM能发展出那么多高级特性的前提——如果不是Java代码最终运行在一个可被JVM全面接管的环境里,像垃圾回收自动管理内存这种能力是没法实现的。
理解了这一层,再看JVM里的各种设计,思路会通畅很多。所有机制的本质,都是在"屏蔽底层差异"和"提高执行效率"这两个目标之间做平衡。
1.2 理清JDK、JRE、JVM三者关系
热搜里有人搜"jre和jvm之间的关系",说明这块确实容易被绕晕。用一句话概括:JDK是Java开发工具包,JRE是Java运行环境,JVM是Java虚拟机。三者的关系就像工厂流水线、车间和核心机器的关系。
| 名称 | 包含内容 | 作用 |
|---|---|---|
| JDK | JRE + 编译器javac + 调试器jdb + 各种诊断工具(jps、jstat、jmap等) | 面向开发者,既负责编译,也负责运行 |
| JRE | JVM + Java标准类库 + 基础运行所需的库文件 | 面向运行者,只负责把编译好的class文件跑起来 |
| JVM | 运行时数据区域、执行引擎、类加载器等 | 真正执行字节码的引擎 |
从JDK 9开始,官方调整了发布形态,引入了模块系统,但JDK、JRE、JVM这套逻辑关系本质上没变。你下载一个JDK安装包,里面就自带了运行所需的JRE;而JVM不管是包含在JDK里还是JRE里,永远是那个真正"干活"的引擎。
复习时我会特别提醒自己一件事:JVM是规范,HotSpot是规范的一个具体实现。面试时说漏嘴把HotSpot和JVM划等号虽然一般不会被揪着不放,但如果你能把两者的层次分清楚,会显得体系感强不少。
1.3 我复习时采用的主线逻辑
JVM的知识点非常多,如果零散地记忆,很容易看过就忘。我自己复习时把整个体系拆成三条主线,上篇先覆盖前两条,第三条放下一部分单独写:
- 数据放哪里:运行时数据区域的划分,以及每块区域的作用和异常表现。
- 数据怎么进来:类从字节码变成可以实例化的Class对象,类加载机制做了什么。
- 数据怎么回收:对象创建之后,JVM如何识别垃圾、如何回收、如何调优。
沿着这条主线,所有细节都能挂到对应的节点上。比如遇到一个OutOfMemoryError,你要能判断它是堆内存溢出、栈溢出、还是元空间溢出,然后对应到具体是哪块运行时数据区域出了问题。能完成这种"报错→知识点"的反向映射,说明这条主线才算真正建立起来了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 运行时数据区域拆解:JVM面试的根据地
2.1 六块核心区域的职责与异常对照
JVM在运行Java程序时,会把内存划分为若干个不同的数据区域,这是《Java虚拟机规范》里定义的基础结构。HotSpot在实现时对规范做了一些调整,比如把方法区用元空间来实现,但整体框架没有变。
这六块区域,每一块负责的事都不同,抛出
