运行时栈帧结构

Java虚拟机以方法作为最基本的执行单元,“栈帧”(Stack Frame)则是用于支持虚拟机进行方法调用和方法执行背后的数据结构,也是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素。

-- 栈帧存储了方法的局部变量表、操作数栈、动态链接、方法返回信息和一些额外的附加信息。

-- 每一个方法从开始执行到结束的过程,都对应着栈帧入栈到出栈的过程。

-- 栈帧需要分配多少内存,在编译时期已经确定。

栈帧的概念结构

1.1 局部变量表

局部变量表是一组变量值的存储空间,存放方法参数和方法内部定义的局部变量。在Java程序被编译为Class文件时,就在方法的Code属性的max_locals数据项中确定了该方法所需分配的局部变量表的最大容量

容量单位:变量槽(Variable Slot),每个变量槽的占用空间没有指定,但是每个变量槽都应该能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据。对于64位的数据,使用两个连续的变量槽。

1.2 操作数栈

操作数栈(Oper and Stack),也叫操作栈,它是一个后入先出(Last In First Out,LIFO)栈。最大深度在编译时被写入Code属性的max_stacks数据项之中。32位数据类型所占栈容量为1,64位数据类型所占栈容量为2。

方法执行:一个方法开始执行时,操作数栈是空的。执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是入栈和出栈的操作。

类型匹配:操作数栈中的元素的数据类型必须与字节码指令的序列严格匹配。编译时期就必须要保证这一点,并且会在类校验阶段的数据流分析中再次验证。

概念模型:两个不同栈帧作为不同方法的虚拟机栈的元素,是相互完全独立的。但是在大多数虚拟机的实现里都会做优化处理,令两个栈的部分操作数栈与上面栈帧的部分局部变量表重叠一起,这样不仅节省了一些空间,更重要的是在进行方法调用时就可以直接公用一部分数据,无需额外的参数传递复制了。如下图所示:

1.3 动态链接

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

Class文件的常量池中存在大量的符号引用,字节码中的方法调用指令就可以以常量池里指向方法的符号引用作为参数。

这些符号引用一部分会在类加载阶段或者第一次使用的时候直接转化为直接引用,这种转化被称为静态解析。另一部分将在每一次运行期间都转化为直接引用,这部分成为动态连接。

1.4 方法返回地址

当一个方法开始执行后,只有两种方式退出:

1、执行引擎遇到任意一个方法返回的字节码指令;

2、方法执行过程中遇到异常,并且这个异常没有在方法体内得到妥善处理,这种方式不会有返回值。

无论哪种退出方式,在方法退出后,都必须返回到最初方法被调用时的位置,程序才能继续执行。

方法返回时可能需要再栈帧中保存一些信息,用来帮助恢复它上层主调方法的执行状态。

正常退出时,主调方法的PC技术器的值就可以作为返回地址,栈帧中很可能会保存这个技术器值;异常退出时,返回地址是需要通过异常处理器表来确定的,栈帧中就一般不会保存这部分信息。

方法退出的过程实际上等同于把当前栈帧出栈,因此退出时可执行的操作有:

  • 恢复上层方法的局部变量表和操作数栈
  • 把返回值压入调用者栈帧的操作数栈中
  • 调整PC计数器的值以指向方法调用指令后面的一条指令等。

1.5 附加信息

《Java虚拟机规范》允许虚拟机实现增加一些规范里没有描述的信息到栈帧之中,例如与调试、性能收集相关的信息,这部分信息完全取决于具体的虚拟机实现,这里不再详述。在讨论概念时,一 般会把动态连接、方法返回地址与其他附加信息全部归为一类,称为栈帧信息。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值