Java虚拟机栈
Java虚拟机栈是JVM 为每个线程分配的私有内存区域,用于存储方法调用的上下文信息。每当线程调用一个 Java 方法时,JVM 会创建一个新的栈帧压入栈顶;方法执行完毕后,栈帧被弹出。栈采用先进后出(FILO)原则。
栈帧的结构
栈帧是栈的基本单位,每个方法调用对应一个栈帧。栈帧在方法开始时创建,在方法结束时销毁。包含的组件:
-
局部变量表
-
一个固定大小的数组,用于存储方法的参数和局部变量。索引从0开始,非静态方法的索引0存储this 引用,后续索引存储参数和局部变量。
-
大小在编译时确定,由方法的字节码指令定义。例如,一个方法有3个参数和2个局部变量,局部变量表大小为5。
-
存储基本数据类型(如int,boolean)和对象引用(reference类型)。基本类型占一个slot(槽位),long和double 占用两个slot。
-

-
操作数栈
-
一个先进后出的栈结构,用于执行字节码指令时的临时数据存储。例如,算术计算会从操作数栈弹出操作数,计算后压入结果。
-
深度在编译时确定。字节码指令如iload (加载int)将值压入栈,istore 将值存储到局部变量表。
-

帧数据
-
动态链接
- 当前类的字节码指令引用了其他类的属性或者方法时,需要将符号引用(编号)转换成对应的运行时常量池中的内存地址。动态链接就保存了编号到运行时常量池的内存地址的映射关系。

-
方法返回地址
- 存储方法退出后应返回的指令地址。对于正常返回,JVM 使用此地址恢复执行;对于异常退出,JVM使用异常表处理。
-
异常表
- 异常表存放的是代码中异常的处理信息,包含了异常捕获的生效范围以及异常发生后跳转到的字节码指令位置。
栈内存溢出
Java虚拟机栈如果栈帧过多,占用内存超过内存可以分配的最大大小就会出现内存溢出。内存溢出报 StackOverflowError错误

如果我们不指定栈的大小,JVM将创建一个具有默认大小的栈。大小取决于操作系统和计算机的体系结构。
要修改 Java虚拟机栈的大小,可以使用虚拟机参数 -Xss
语法:-Xss 栈大小
单位:字节(默认,必须是1024倍数)、k或者K(KB)、m或者M(MB)、g或者G(GB)
例如:-Xss1048576 -Xss1024K
OutOfMemoryError:
-
JVM 允许动态扩展栈容量,但无法申请到足够内存(如系统内存耗尽)
-
创建新线程时无法为栈分配初始内存(例如创建大量线程且 -Xss 设置过大,导致堆外内存不足)
本地方法栈
Java虚拟机栈存储了 Java 方法调用的栈帧,而本地方法栈存储的是native 本地方法的栈帧。
在 Hotspot 虚拟机中,Java虚拟机栈和本地方法栈实际上使用了同一个栈空间。本地方法栈用在内存上生成一个栈帧,临时保存方法的参数同时方便出现异常时也把本地方法的栈信息打印出来。