深入理解 Java 虚拟机 (JVM) 核心原理
1. 概述:JVM 到底是什么?
JVM(Java Virtual Machine)本质上是一个运行在操作系统之上的虚拟计算机。
我们编写的 .java 源码文件,会被编译器编译成 .class 字节码文件。JVM 的核心作用,就是将这些字节码翻译成当前操作系统(Windows、Linux、macOS 等)能够理解的机器码并执行。这就是 Java 实现'一次编写,到处运行'的核心。
JVM 的整体架构主要由三个核心子系统组成:
- 类加载子系统 (Class Loader Subsystem): 负责把
.class文件搬进内存。 - 运行时数据区 (Runtime Data Area): 负责在内存中安顿这些数据(也就是我们前面聊过的内存结构)。
- 执行引擎 (Execution Engine): 负责执行代码逻辑,并清理垃圾(GC)。
2. 第一站:类加载子系统 (Class Loading)
要想运行程序,首先得把代码加载进内存。类加载的过程并不只是简单的'读文件',它分为三个严格的阶段:
- 加载 (Loading): 通过类的全限定名获取定义此类的二进制字节流,并将其转化为方法区中的运行时数据结构,最后在堆中生成一个代表这个类的
java.lang.Class对象。 - 链接 (Linking):
- 验证 (Verify): 检查字节码是否符合 JVM 规范,保证安全(不损坏虚拟机)。
- 准备 (Prepare): 为类中的静态变量分配内存,并设置默认初始值(例如
int设为0,boolean设为false)。 - 解析 (Resolve): 将常量池内的符号引用(比如简单的名字)替换为直接引用(内存指针)。
- 初始化 (Initialization): 真正开始执行类中定义的 Java 代码,为静态变量赋予程序员指定的初始值,并执行静态代码块 (
static {})。
💡 核心机制:双亲委派模型 (Parent Delegation Model)
当一个类加载器收到加载请求时,它不会自己先去加载,而是把请求向上委托给父加载器。只有当父加载器找不到该类时,子加载器才会尝试自己去加载。
为什么这样做? 为了安全和避免重复加载。例如,用户自己写了一个
java.lang.String类,由于双亲委派,JVM 最终会委托给最顶层的启动类加载器去加载系统自带的String类,从而防止了核心 API 被恶意篡改。
3. 第二站:运行时数据区 (Runtime Data Area)
类被加载后,数据需要存放在内存中。JVM 内存被精细地划分为'公有'和'私有'两部分:
3.1 线程私有区域(生命周期与线程相同)
- 程序计数器 (PC Register): 内存空间极小。它是当前线程所执行的字节码的行号指示器。多线程切换时,靠它来记住'刚才执行到哪了'。(唯一不会发生内存溢出 OOM 的区域)
- 虚拟机栈 (JVM Stack): 控制 Java 方法的运行。每次调用方法都会创建一个栈帧(存放局部变量表、操作数栈、动态链接、方法出口信息)。方法结束,栈帧出栈。
- 作用与虚拟机栈类似,只不过它服务的是 JVM 调用的底层 C/C++ 方法(Native 方法)。


