一、概述
1.1 概念
JVM 栈描述的是每个线程 Java 方法执行的内存模型:每个方法被执行的时候,JVM 会同步创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口 等信息。
栈是运行时单位,而堆是存储的单位,即栈解决的是运行问题,即程序如何执行,或者如何处理数据,功能类似于计算机硬件 PC寄存器。堆解决的是数据存储的问题,即数据怎么放、放哪儿。
1.2 特点
- 访问速度快,仅次于程序计数器
- 线程私有
- 存在 OOM,不存在 GC
1.3 栈的存储单位——栈帧
每个线程都有自己的栈,栈中的数据都是以栈帧的格式存在的,栈帧中存储局部变量表、操作数栈、动态链接、方法出口等信息。调用一个方法,就会创建一个栈帧。
栈帧的大小会随着它里面存储的数据的大小而发生变化,如果存储的数据越大,栈帧越大。
1.4 栈的两类异常
-
如果虚拟机栈容量不可以动态扩展,当线程请求的栈深度大于虚拟机允许的最大容量,就会抛出 StackOverFlowError 异常;
-
如果虚拟机栈容量可以动态扩展,当栈无法申请到足够的内存会抛出 OutOfMemoryError 异常。
HotSpot 虚拟机不支持动态扩展,所以只有在创建线程申请内存时因为无法获得足够的内存才会导致 OutOfMemoryError 异常。
二、局部变量表
2.1 概念
局部变量表是一个数组,主要用于存储方法参数和定义在方法体内的局部变量,包括基本数据类型、对象引用以及 returnAddress 类型。
2.2 关于槽的理解
局部变量表最基本的单位是槽,32位以内的类型只占用一个槽,64位的类型(double 和 long)占用两个槽。
JVM 会位局部变量表中的每一个 Slot 按照局部变量定义的顺序分配访问索引,通过这个索引即可以访问局部变量表中指定的局部变量。
对于非静态方法来说,0 号索引存储的是 this。
举例:定义一段程序:
public void test1(int a, int b) {
short c = 5;
boolean d = true;
char e = 'c';
byte f = 'c';
int g = 10;
float h = 10.0f;
double i = 10.00;
long j = 1000000000;
int test = 5;
}
如上图,从编译后的文件中可以看到,除了double 和 long 类型的数据,其他数据都只占用一个槽。
2.3 局部变量表特点
- 局部变量表所需的内存空间(槽数量)在编译期间就已经确定下来了。
- 局部变量表中的变量是重要的垃圾回收的根节点,只要被局部变量表中直接或间接引用的对象都不会被回收。