虚拟机栈、本地方法栈与程序计数器是jvm中三个线程私有的内存区域:虚拟机栈用于java方法执行,含局部变量表、操作数栈等;本地方法栈服务native方法,hotspot中与虚拟机栈合一;程序计数器记录线程下一条字节码指令地址,是唯一不溢出的区域。

Java 虚拟机规范中定义的虚拟机栈、本地方法栈与程序计数器,是 JVM 运行时数据区中三个线程私有的内存区域。它们不共享、不跨线程,各自服务于线程执行的不同环节,共同支撑 Java 方法调用、指令执行和线程切换的底层机制。
虚拟机栈:Java 方法执行的“工作台”
虚拟机栈为每个线程单独分配,生命周期与线程一致。它的核心作用是支持 Java 字节码方法的执行。
- 每次调用一个 Java 方法,JVM 就为其创建一个栈帧(Stack Frame),压入该线程的虚拟机栈;
- 栈帧里包含四类关键内容:
- 局部变量表:存方法参数和内部定义的局部变量(不包括成员变量);
-
操作数栈:方法执行过程中的临时运算空间,比如
i + j的中间结果就暂存在这里; - 动态链接:指向运行时常量池中该方法的符号引用,用于支持多态和方法解析;
- 方法出口信息:记录方法返回后应跳转到的字节码地址(正常返回或异常退出);
- 方法执行结束,对应栈帧自动出栈,内存立即释放;
- 栈深度受限于栈容量:递归过深或方法调用链太长,会触发
StackOverflowError;若线程太多且栈空间设得过大,可能耗尽内存导致OutOfMemoryError。
举例:
main()调用foo(),foo()又调用bar(),此时栈中自底向上依次是main→foo→bar三个栈帧;bar()返回后,其栈帧弹出,控制权交还给foo的栈帧。
本地方法栈:为 native 方法服务的“专用通道”
本地方法栈的功能与虚拟机栈高度相似,但服务对象不同:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 虚拟机栈服务的是 Java 编写的、被编译为字节码的方法;
- 本地方法栈服务的是用其他语言(如 C/C++)实现的 native 方法(通过
native关键字声明,由 JNI 调用); - JVM 规范未强制规定其实现方式——HotSpot 虚拟机从 JDK 8 起已将二者合二为一(即共用同一块栈内存),但逻辑上仍区分用途;
- 同样会抛出
StackOverflowError(栈深度超限)和OutOfMemoryError(扩展失败); - 对 Java 开发者而言,除非直接使用 JNI 或调试底层交互,一般无需主动干预本地方法栈。
注意:
Object.wait()、Thread.start()等看似 Java 方法,底层实际调用了 native 实现,其执行上下文就落在本地方法栈中。
程序计数器:线程执行位置的“导航指针”
程序计数器(PC Register)是一块极小、固定大小的内存,是 JVM 规范中唯一不会发生内存溢出的区域。
- 它的作用非常明确:记录当前线程下一条要执行的字节码指令地址;
- 如果线程正在执行 Java 方法,PC 计数器保存的是字节码指令的偏移地址(如
iconst_1所在位置); - 如果正在执行 native 方法,PC 值为
undefined(无意义); - 多线程靠时间片轮转调度,每次线程被挂起再恢复时,全靠 PC 计数器记住“刚才执行到哪了”,才能无缝续跑;
- 因为每个线程必须独立维护自己的执行位置,所以它是线程私有的;
- 它不存储变量、不参与运算,也不存放对象引用,纯粹是个“指针”。
类比:就像看书时夹的书签——换人看(线程切换)时,每人用自己的书签,互不干扰。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










