概述
- 虚拟机栈是线程私有的,他的生命周期与线程的相同。
- 每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧。当方法执行完毕之后,栈帧出栈。
- 栈帧包括局部变量表、操作数栈、动态链接、方法返回地址等信息。
虚拟机栈中会出现两种异常:
StackOverflowError
:线程请求的栈的深度大于虚拟机所允许的深度。OutOfMemoryError
:如果虚拟机栈的容量是可以动态扩展的,当栈扩展时无法申请到足够的内存会出现此异常。
设置栈的内存大小:
-Xss:大小
栈帧
栈帧是Java虚拟机栈的栈元素,是用于支持虚拟机进行方法调用和执行的数据结构。
栈帧中存储了这个方法运行阶段所需要的全部信息,每个方法从开始调用到执行结束就是一个栈帧入栈到出栈的过程。
局部变量表
局部变量表是一组变量存储空间,用来存储方法参数和方法内部定义的局部变量。局部变量表的大小在编译阶段就已经确定了。
局部变量表存储单位是变量槽,大约是叫Variable Slot,每个槽的容量是32位。对于数据类型其有以下的规律:
类型 | 大小 |
---|---|
boolean | 一个槽,32位 |
byte | 一个槽,32位 |
char | 一个槽,32位 |
short | 一个槽,32位 |
int | 一个槽,32位 |
long | 两个槽,64位 |
float | 一个槽,32位 |
double | 两个槽,64位 |
引用类型 | 一个槽,32位 |
另外,对于所有非静态的方法,其局部变量表的第一行都是this这个引用类型,指向方法本身。
操作数栈
Java虚拟机的解释引擎是基于栈的执行引擎,这个栈就是指操作数栈。
操作数栈可以用来存储各种数据,其中32位的数据所占的栈的容量为1,64位的为2;运行期间的任何操作都不会超出操作数栈的深度,这个深度值在编译期间就已经被确定下来,并保存为max_stacks属性的值。
操作数栈主要用来保持运行过程中的中间结果,在执行的过程中,会有各种字节码指令往操作数栈中写入和读取数据,即出入栈操作。
动态连接
这个其实是一个指向运行时常量池的方法引用,持有这个引用的目的是为了支持方法调用过程中的动态连接,字节码文件的常量池中包含有大量的符合引用,这些引用在类加载的过程中加载到内存中,成为运行时常量池。字节码中的方法调用指令就以常量池中指向方法的符号引用作为参数。
这些符号引用一部分会在类加载阶段或者第一次使用的时候就转化为直接引用,这种转化称为静态解析。另外一部分将在每一次运行期间转化为直接引用,这部分称为动态连接。
方法返回地址
在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行,方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般方法返回地址中会存储此方法被调用时PC寄存器中的值。
在方法被调用之后有两种情况可以退出这个方法:
- 正常完成出口
遇到返回指令的字节码,正常执行完成,结束方法。正常完成出口退出时有可能会有返回值返回给方法的调用者。 - 异常完成出口
出现异常,并且这个异常不能够被当前方法处理,这个时候也会退出当前方法。异常完成出口退出时,不会给上层调用者任何返回值。
栈顶缓存技术
因为Java虚拟机采用的是零地址指令集,这就会产生更多的指令分派和内存读写;频繁的进行内存读写必然会影响执行速度。
为了提高执行速度,然后就产生了栈顶缓存技术,将栈顶元素全部保存在物理CPU的寄存器中,以此降低对内存的读写次数,提高执行效率。