1. 为什么学JVM要先弄懂“组成”
我刚接触Java那会儿,对JVM的理解就是一串能背下来的名词:“类加载器、运行时数据区、执行引擎、垃圾回收”。面试前背得滚瓜烂熟,可真到线上服务内存溢出(OOM)、启动直接报错、或者同事问我“为什么这里改成接口调用就变慢了”,脑子里的那套名词根本派不上用场。后来我才意识到,问题不在于记没记住名词,而在于我没有把JVM当成一台“机器”去理解——不知道它的零件有哪些、每个零件干什么、零件之间怎么配合,自然也就谈不上排查故障。
“JVM的组成”这个话题,看起来是入门第一课,其实是一张地图。你后面遇到的所有问题——堆内存溢出、栈溢出、类加载冲突、JIT编译异常、GC停顿——几乎都能在这张地图上找到对应的位置。理解了组成,你才能知道OOM该去看哪块区域、类为什么加载不进来该查哪个环节、线程卡住该抓哪里的信息。这也是为什么很多公司面试Java岗位,第一问就是JVM由哪几部分构成,它不是简单考察背诵,而是想看看你有没有建立这张地图。
这篇是我“JVM笔记”系列的第一篇,重点就放在组成上:JVM整体分哪几个子系统、运行时数据区里各块区域是干什么的、类加载机制是怎么把.class变成可运行对象的、执行引擎和垃圾回收在JVM里扮演什么角色。为了让内容不只是停留在概念层,我会穿插一些实际排查时用到的参数、命令和场景,毕竟能解决问题的笔记才算有价值。
顺带说一句,如果你分不清JVM、JRE、JDK三者的关系,这篇也会顺带讲清楚。JDK是Java开发工具包,里面包含了编译器javac和一系列开发工具;JRE是Java运行环境,只负责跑Java程序;而JVM是JRE的核心,真正执行Java字节码的那个“虚拟机”。简单说,JVM在JRE里,JRE在JDK里,而JVM本身又由下面要讲的几个子系统组成。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. JVM整体架构:三个子系统加一块内存区域
先抛开细节,从上往下看JVM的整体结构。一台真实的计算机有CPU、内存、磁盘、操作系统,JVM作为一台“虚拟计算机”,它的结构也对应着一台完整计算机该有的东西:能加载程序的装置、能存放运行数据的空间、能执行指令的处理单元,以及跟外部打交道的通道。
2.1 类加载子系统:Java类的“安检通道”
类加载子系统负责把.class文件从磁盘、网络或者其他地方读进来,经过一系列校验和准备,最终生成JVM能够使用的Class对象。这个过程包括加载、链接(验证、准备、解析)、初始化三个阶段,后面我会专门用一整章来拆。
你可以把它理解为机场安检:旅客(字节码)经过身份核验(验证)、行李称重(准备)、确认航班信息(解析)、最后登机(初始化),每一步都不能少。没有类加载子系统,你在代码里写的任何一个类都进不了JVM。
这里有一个新手容易忽略的点:类加载是“按需”的,不是程序一启动就把所有类全部加载进内存。只有当一个类被真正使用到的时候,才会触发它的加载和初始化。这也是为什么一个大型应用启动时类加载会比较慢,但启动之后运行就流畅了——大部分类已经在运行过程中被陆续加载完了。
2.2 运行时数据区:程序运行时的“工作现场”
类加载进来之后,程序要开始跑,就得有地方存放数据。运行时数据区就是JVM在运行期间管理内存的各个区域,下面几个区域是它的主体:
- 程序计数器(Program Counter Register)
- 虚拟机栈(JVM Stack)
- 本地方法栈(Native Method Stack)
- 堆(Heap)
- 方法区(Method Area,JDK 8之后叫元空间Metaspace)
前三个是线程私有的,跟着线程生而生、线程灭而灭;堆和方法区是所有线程共享的,这两块内存的管理直接影响整个应用的稳定性。堆是GC(垃圾回收)的主战场,方法区存放类信息、常量、静态变量等数据。具体每一块是什么、怎么调参,下一章展开说。
2.3 执行引擎与本地接口:翻译字节码的“CPU”和对外通道
类加载好、数据也放进内存了,接下来要真正“执行”。执行引擎就是JVM的CPU,它负责解释执行或者编译执行字节码指令。JVM支持两种执行方式,一种是解释执行,相当于一句一句翻译;另一种是JIT编译,把热点代码编译成本地机器码,执行效率更高。现代JVM通常两者结合,先解释执行,运行过程中发现某段代码经常执行(热点代码),就触发了JIT编译。
垃圾回收器也是执行引擎的一部分——内存中那些不会再被引用的对象,需要有人清理掉,这就是GC做的事。你听过的Serial、Parallel、CMS、G1、ZGC,这些都是JVM里负责回收内存的收集器实现。
本地方法接口和本地方法库,则是JVM跟操作系统底层打交道的通道。Java不是所有功能都能自己实现,比如获取系统时间、操作文件、网络通信这些能力,最终都是通过native方法调用到C/C++写的本地库去完成的。你在代码里看到的所有方法,只要是以native关键字声明的,它的实现就在JVM之外。
2.4 为什么“类加载子系统 + 运行时数据区 + 执行引擎”是完整的机器
把这几个子系统合在一起,JVM就构成了一台完整的“虚拟计算机”:
- 类加载子系统负责“装系统”——把程序需要的类从外部装进来
- 运行时数据区负责“提供场地”——数据有地方放,指令有地方执行
- 执行引擎负责“跑任务”——真正逐条解释、编译和回收
所以,回答“JVM的组成”这个问题,不能只说“运行时数据区包含堆、栈、方法区”,那只是其中一块。完整的答案应该有三条主线:类加载子系统、运行时数据区、执行引擎,再加上与操作系统交互的本地接口。面
