JVM之虚拟机栈

帅比萌擦擦* 提交于 2019-12-24 16:39:18

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

        java虚拟机栈描述的是java方法执行的是内存模型,它也是线程私有的。虚拟机栈是用于存放java方法信息的一块内存区域,因为方法的执行是线程级别的,所以虚拟机栈的生命周期与线程的生命周期相同。在每个方法在执行的时候都会创建一个栈帧。在栈帧中包含了局部变量表,操作数栈,动态链接,returnAddress(方法返回值),和一些额外的附加信息
        在编译程序代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到方法的code(方法表:java代码编译成的字节指令)中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决与具体的虚拟机实现。一个线程中的方法调用链可能会很长,很多方法都同时处于执行状态。对于执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是有效的,称为:当前栈帧,,与这个栈帧关联的方法成为:当前方法。执行引擎运行的所有字节码指令都只对当前栈帧进行操作。
    在Java虚拟机规范中对这块区域定义了两种异常状况
    第一种:如果线程请求的栈深度大于虚拟机允许的深度,将抛出StackOverflowError异常;
    第二种:如果虚拟机栈申请动态扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

关于栈帧中的信息如下图:

1)局部变量表
        是一组变量值存储空间,用于存放:方法参数、方法内部定义的局部变量。
        局部变量表的容量以变量槽(Variable Slot)为最小单位,虚拟机规范中并没有明确指明一个Slot应占用的内存空间大小,
只是说到每个Slot都应该能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据。
        虚拟机通过索引定位的方式使用局部变量表,索引值的范围从0开始到变量表最大的Slot数量。如果访问的是32为数据类型的变量,
索引n就代表使用第n个Slot,如果是64位数据类型的变量,则说明会同时使用n和n+1两个Slot(64位变量只有long和double,会分割存储,
把long和double分割为两次32位读写的做法,这两次分割就对应了n和n+1)。对于两个相邻的共同存放一个64位数据的两个Slot,不允许采用
任何方式单独访问其中某一个,否则会在类加载的校验阶段抛出异常。
        局部变量表中的第0位索引的Slot索引的Slot默认用于传递方法所属对象实例的引用,在方法中可以用关键“this”来访问这个隐含的参数。
        为了尽可能节省栈帧空间,局部变量表中的Slot是可以重用的。

2)操作数栈
        操作数栈是一个LIFO栈,栈内每个元素可以是任意的Java数据类型,包括long和double,32位数据类型栈的容量位1,64位为2.
当一个方法开始执行的时候,这个操作数栈内是空的,在方法的执行过程中,会有各种字节码指令在操作数栈中写入和提取,也就是出栈和入栈。

3)动态链接
        每个栈帧都包含一个指向运行时常量池中的该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态链接。

4)方法返回地址(returnAddress)
        方法的返回分为两种:正常完成出口,异常完成出口。正常完成出口就是成功执行到了return。异常完成出口是值在return之前有异常,
而且异常没有在方法体内得到解决导致的方法退出。
        方法退出的过程就等同于当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)
压入调用者栈帧的操作数栈中,调整pc计数器的值以指向方法调用指令后面的一条指令。

5)附加信息
        虚拟机规范允许具体的虚拟机实现中增加一些规范里没有的描述信息到栈帧中,例如与调试相关的等等。这部分信息完全取决于具体的虚拟机实现,
在实际开发中,一般把动态链接,方法返地址和附加信息全部归为一类,称为:栈帧信息。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!