Java虚拟机栈

一 Java虚拟机栈概念。


Java虚拟机栈中存储的内容,它用于存储数据和部分过程结果的数据结构,同时也被用来处理动态链接/方法返回值和异常分派.

一个完整的栈帧包括:局部变量表/操作数栈/动态链接信息/方法正常完成和异常完成信息.

Java虚拟机栈是描述Java方法运行过程的内存模型.Java虚拟机会为每一个即将运行的Java方法创建一块叫做”栈帧”的区域,这块区域用于存储该方法在运行过程中所需要的一些信息,这些信息包括:

局部变量表(存放基本数据类型变量\引用类型的变量\returnAddress类型的变量)
操作数栈
动态链接
方法出口

异常完成信息


二 局部变量表


局部变量表:(由若干个Slot组成,长度由编译器决定,单个Slot可以存储一个类型为boolean/byte/char/short/float/reference/returnAddress的数据,两个Slot可以存储一个类型为long或者double的数据)

存放编译期可知的各种基本数据类型、对象引用类型和returnAddress类型(指向一条字节码指令的地址:函数返回地址)。
long、double占用两个局部变量控件Slot。
局部变量表所需的内存空间在编译期确定,当进入一个方法时,方法在栈帧中所需要分配的局部变量控件是完全确定的,不可动态改变大小。
异常:线程请求的栈帧深度大于虚拟机所允许的深度—StackOverFlowError,如果虚拟机栈可以动态扩展(大部分虚拟机允许动态扩展,也可以设置固定大小的虚拟机栈),但是无法申请到足够的内存—OutOfMemorError。
局部变量表是一组变量值的存储空间,它用于存储方法,参数,以及方法内部定义的局部变量。在java编译器编译class的时候就在该方法的code属性中确定了该方法所需的最大容量,局部变量表中的变量槽也就是Slot为最小单位,java虚拟机中并没有指明一个Slot所需要占用的内存空间大小,只是非常有导向性的描述到任何一个Slot都应该能存放一个布尔型,字节型,字符型,短整型,整形,浮点型,reference类型以及returnAddress类型的数据在这8种数据类型之中,他们的共同特征是都可以使用32位或者更小的内存空间来进行存放,但Java虚拟机这样描述Slot的空间与明确指出每个Slot应该占用32位内存还是有差别的,他这种描述允许Slot的内存空间随着处理器,操作系统或者java虚拟机的具体实现而发生变化,假如在64位的java虚拟机之中使用了64位的内存空间去实现一个Slot,java虚拟机仍然要使用对齐或者补白的手段,让Slot在外观上看出来了与32位的java虚拟机之中是一致的。

三 操作数栈


后进先出LIFO,最大深度由编译期确定。栈帧刚建立时,操作数栈为空,执行方法操作时,操作数栈用于存放JVM从局部变量表复制的常量或者变量,提供提取,及结果入栈,也用于存放调用方法需要的参数及接受方法返回的结果。
操作数栈可以存放一个jvm中定义的任意数据类型的值。
在任意时刻,操作数栈都一个固定的栈深度基本类型除了long、double占用两个深度,其它占用一个深度


四 动态连接


每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。Class文件的常量池中存在有大量的符号引用,字节码中的方法调用指令就以常量池中指向方法的符号引用为参数。这些符号引用,一部分会在类加载阶段或第一次使用的时候转化为直接引用(如final、static域等),称为静态解析,另一部分将在每一次的运行期间转化为直接引用,这部分称为动态连接。


五 方法返回地址


当一个方法被执行后,有两种方式退出该方法:执行引擎遇到了任意一个方法返回的字节码指令或遇到了异常,并且该异常没有在方法体内得到处理。无论采用何种退出方式,在方法退出之后,都需要返回到方法被调用的位置,程序才能继续执行。方法返回时可能需要在栈帧中保存一些信息,用来帮助恢复它的上层方法的执行状态。一般来说,方法正常退出时,调用者的PC计数器的值就可以作为返回地址,栈帧中很可能保存了这个计数器值,而方法异常退出时,返回地址是要通过异常处理器来确定的,栈帧中一般不会保存这部分信息。
方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,如果有返回值,则把它压入调用者栈帧的操作数栈中,调整PC计数器的值以指向方法调用指令后面的一条指令。
当一个方法即将被运行时,Java虚拟机首先会在Java虚拟机中为该方法创建一块”栈帧”,栈帧中包含局部变量表\操作数栈\动态链接\方法出口信息等.当方法在运行过程中需要创建局部变量时,就将局部变量的值存入栈帧的局部变量表中.

当这个方法执行完毕后,这个方法所对应的栈帧将会出栈,并释放内存空间.

经常会有人说:Java内存空间分为”栈”和”堆”,栈中存放局部变量,堆中存放对象.

上述表达并不完全正确!这里的堆可以这么理解,但是这里的栈只代表了Java虚拟机栈中的局部变量表部分.真正的Java虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表/操作数栈/动态链接/方法出口信息.

六 Java虚拟机栈特点
1 Java虚拟机栈是线程私有的,每个线程都有各自的Java虚拟机栈,而且随着线程的创建而创建,随便线程的消亡而消亡.
2 Java虚拟机栈会出现两种异常OutOfMemoryError和StackOverflowError
a) 如果Java虚拟机栈的内存大小不允许动态扩展,那么当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就会抛出StackOverflowError异常.

b) 如果Java虚拟机栈的内存大小允许动态扩展,且当线程请求栈是内存用完了,无法再动态扩展了,此时就会抛出OutOfMemoryError异常.

3 后进先出(LIFO)栈:
java虚拟机栈和程序计数器一样,他是线程私有的内存区域,他的生命周期与线程相同,java虚拟机栈描述的是java方法执行时候的内存概念模型,每个方法在执行的时候都会创建一个栈帧,用来创建这个方法的操作数栈,局部变量表,方法出口,动态链接等信息,每一个方法在调用和结束的过程就对应了一个栈帧在虚拟机栈中入栈到出栈的过程,java虚拟机栈是一个后进先出栈,靠后执行的方法会优先完成,后面进入虚拟机栈的栈帧优先被出栈,这与我们平时执行java方法的印象是一致的,在程序执行中java方法的调用,执行和退出,都与java虚拟机栈里面存储的栈帧有着密切的联系。

4 局部变量表的创建是在方法被执行的时候,随着栈帧的创建而创建.而且,局部变量表的大小在编译时期就可以确定下来了,在创建的时候只需要分配实现规定好的大小即可.此外,在方法运行过程中局部变量表的大小是不会发生改变的.
OutOfMemoryError和StackOverflowError区别?

StackOverflowError表示当前线程申请的栈超过了事先定好的栈的最大深度,但是内存空间可能还有很多.而OutOfMemoryError是指当前线程申请栈时发现栈已经满了,而且内存也都已经用光了.

你可能感兴趣的:(JVM)