JVM 运行时数据区域,通常也被称为内存布局,是理解 Java 并发与性能优化的基石。需要注意的是,它与 Java 内存模型(JMM)是两个完全不同的概念。该区域主要由以下五大部分组成:

1. 程序计数器(线程私有)
程序计数器的核心作用是记录当前线程执行的行号。它是一块较小的内存空间,可视为当前线程所执行字节码的行号指示器。
如果当前线程正在执行的是 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是 Native 方法,这个计数器值为 undefined。值得注意的是,程序计数器是唯一一个在 JVM 规范中没有规定任何 OOM(内存溢出)情况的区域。
在 JVM 中,多线程是通过线程轮流切换来获得 CPU 执行时间的。因此,在任一具体时刻,一个 CPU 内核只会执行一条线程中的指令。为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,且不能互相干扰。
我们通过一段简单的代码及对应的字节码指令来看看它的作用:
public class PcDemo {
public static int add(int a, int b) {
return a + b;
}
}
对应的字节码指令大致如下:
0: iload_0 // 从局部变量表中加载变量 a 到操作数栈
1: iload_1 // 从局部变量表中加载变量 b 到操作数栈
2: iadd // 两数相加
3: ireturn // 返回
执行流程如下:
- 初始状态:当方法开始执行时,PC 计数器设置为 0,指向第一条指令
iload_0。 - 执行第一条指令:将局部变量表中索引为 0 的整数(即参数 a)加载到操作数栈顶。完成后 PC 更新为 1。
- 执行第二条指令:将索引为 1 的整数(即参数 b)加载到操作数栈顶。完成后 PC 更新为 2。
- 执行第三条指令:弹出栈顶两个整数相加,结果压入栈顶。完成后 PC 更新为 3。
- 执行最后一条指令:弹出结果作为返回值,方法结束。
若发生线程切换,例如线程 A 和线程 B 同时运行:
- 线程 A 的 PC = 2(下一条应执行
iadd) - 线程 B 的 PC = 4(假设其已执行完,或处于不同状态)
这确保了每个线程都能独立维护自己的执行上下文。
2. Java 虚拟机栈(线程私有)
Java 虚拟机栈(JVM Stack)是线程私有的运行时数据区,生命周期与线程相同。当一个方法被调用时,JVM 会为该方法创建一个栈帧(Stack Frame)并压入虚拟机栈中。栈帧主要包含局部变量表、操作数栈、动态链接和方法返回地址等信息。当方法执行结束时,对应的栈帧会被弹出。



