概述
优缺点
- 优点是跨平台,指令集小,编译器容易实现
- 缺点是性能下降,实现同样的功能需要更多的指令
栈vs堆
栈是运行时的单位,而堆是存储的单位
基本内容
- 内部保存一个个的栈帧(Stack Frame),对应着一次次的Java方法调用
- 栈顶 即为当前方法
- 作用:主管Java程序的运行,它保存方法的局部变量(8 种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回
- 访问速度仅次于程序计数器
- 不需要GC,但是可能存在OOM
可能出现的异常
Java 虚拟机规范允许Java栈的大小是动态的或者是固定不变的
- 固定不变:如果线程请求分配的栈容量超过Java虚拟机栈允许的最大容量,Java虚拟机将会抛出一个StackoverflowError 异常
- 动态:在尝试扩展的时候无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那Java虚拟机将会抛出一个 OutofMemoryError 异常
设置栈内存大小
使用参数 -Xss 选项来设置线程的最大栈空间,栈的大小直接决定了函数调用的最大可达深度
栈的存储单位
存储什么
栈帧是一个内存区块,是一个数据集,维系着方法执行过程中的各种数据信息
运行原理
- 在一条活动线程中,一个时间点上,只会有一个活动的栈帧(栈顶)。当前栈帧 -- 当前方法 -- 当前类
- 执行引擎运行的所有字节码指令只针对当前栈帧进行操作
- 不同线程中所包含的栈帧是不允许存在相互引用的,因为是私有的
- 方法有两种返回方式
- 正常的函数返回,使用return
- 出现未捕获处理的异常,以抛出异常的方式结束
- 都会弹出栈帧
栈帧的内部结构
五部分:
- 局部变量表(Local Variables)
- 操作数栈(Operand Stack)(或表达式栈)
- 动态链接(Dynamic Linking)(或指向运行时常量池的方法引用)
- 方法返回地址(Return Address)(或方法正常退出或者异常退出的定义)
- 附加信息
局部变量表LV
- 定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量,这些数据类型包括各类基本数据类型、对象引用(reference),以及returnAddress返回值类型
- 因为私有,不存在数据安全问题
- 局部变量表所需的容量大小是在编译期确定下来的,并保存在方法的Code属性的maximum local variables数据项中。在方法运行期间是不会改变局部变量表的大小的。
- 局部变量表中的变量只在当前方法调用中有效
slot
- 局部变量表,最基本的存储单元是Slot(变量槽)
- 在局部变量表里,32位以内的类型只占用一个slot(包括returnAddress类型),64位的类型占用两个slot(long和double)
- 如果当前帧是由构造方法或者实例方法创建的,那么该对象引用 this 将会存放在index为0的slot处
- 解释了为什么静态方法中不能使用 this,因为局部变量表里没有
- 栈帧中的局部变量表中的槽位是可以重用的,如果一个局部变量过了其作用域,那么在其作用域之后申明新的局部变量变就很有可能会复用过期局部变量的槽位,从而达到节省资源的目的。
public void test4() {
int a = 0;
{
int b = 0;
b = a + 1;
} //b过了这个代码块就被销毁了,但slot还在
//变量c使用b的位置
int c = a + 1;
}
变量的分类
按照在类中声明的位置:
- 成员变量:使用前,默认初始化赋值
- 类变量:被 static 修饰,linking的prepare阶段:给类变量默认赋值 ---> initial阶段:给类变量显式赋值即静态代码块赋值
- 实例变量:随着对象的创建,会在堆空间中分配实例变量空间,并进行默认赋值
- 局部变量:使用前,必须要进行显式赋值
补充
- 在方法执行时,虚拟机使用局部变量表完成方法的传递
- 局部变量表中的变量是重要的垃圾回收根节点,只要被局部变量表中直接或间接引用的对象都不会被回收
操作数栈OS
- 根据字节码指令,往栈中写入数据或提取数据
- 主要用于保存计算过程的中间结果,同时作为计算过程中变量临时的存储空间
- 每一个操作数栈都会拥有一个明确的栈深度用于存储数值,其所需的最大深度在编译期就定义好了,保存在方法的Code属性中,为maxstack的值
- 如果被调用的方法带有返回值的话,其返回值将会被压入当前栈帧的操作数栈中,并更新PC寄存器中下一条需要执行的字节码指令
- Java虚拟机的解释引擎是基于栈的执行引擎,其中的栈指的就是操作数栈
栈顶缓存技术
- 由于操作数是存储在内存中的,因此频繁地执行内存读/写操作必然会影响执行速度
- 栈顶缓存(Tos,Top-of-Stack Cashing)技术:将栈顶元素全部缓存在物理CPU的寄存器中,以此降低对内存的读/写次数,提升执行引擎的执行效率
- 寄存器的优点:指令更少,执行速度快