深入了解JAVA虚拟机学习笔记26——字节码执行引擎(栈帧)

作者 : 开心源码 本文共2173个字,预计阅读时间需要6分钟 发布时间: 2022-05-11 共76人阅读

每天进步一点点!

今天主要理解一下栈帧中的内容,大部分为内容都比较容易了解,且做以下记录。

首先详情以下栈帧的概念:

栈帧(Stack Frame)是使用于支持虚拟机进行方法调使用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素。

栈帧中主要存储了5方面的内容:方法的局部变量表,操作数栈,动态连接,方法返回地址和少量额外的附加信息。

在编译代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到了方法表的Code属性中,因而一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体虚拟机的实现。

一个线程中的方法调使用链可可以会很长,很多方法都同时处于执行状态。对于执行引擎来讲,活动线程中,只有虚拟机栈顶的栈帧才是有效的,称为当前栈帧(Current

Stack Frame),这个栈帧所关联的方法称为当前方法(Current

Method)。执行引使用所运行的所有字节码指令都只针对当前栈帧进行操作。

栈帧的概念结构如下图所示:

下面让我们来简单详情以下,栈帧中的五个方面。

1. 局部变量表:是一组变量值存储空间,使用于存放方法参数和方法内部定义的局部变量。

在方法的Code属性的max_locals数据项中,确定了该方法所需要分配的局部变量表的最大容量。

局部变量表的容量以变量槽(Variable Slot,下称Slot)为最小单位,slot的长度随解决器,操作系统和虚拟机的不同而变化。

注意:Slot是能重使用的,当前字节码PC计数器的值已经超出了某个变量的作使用域,那这个变量对应的Slot即可以交给其余变量用。

对于64位的数据类型,虚拟机会以高位对齐的方式为其分配两个连续的Slot空间。

虚拟机的引使用需要做到两点:

一是从此引使用中直接或者间接地查找到对象在Java堆中的数据存放的起始地址索引;

二是此引使用中直接或者间接地查找到对象所属数据类型在方法区中的存储的类型信息。

局部变量表建立在线程的堆栈上,是线程私有的数据,无论读写两个连续的Slot能否为原子操作,都不会引起数据安全问题。

局部变量表,索引值的范围是从0开始至局部变量表最大的Slot数量。

假如是64位数据类型的变量,会同时用n和n+1两个Slot。

假如执行的是实例方法(非static的方法),那局部变量表中第0位索引的Slot默认是使用于传递方法所属对象实例的引使用。

局部变量不像前面详情的类变量那样存在“准备阶段”,一次在准备阶段,赋予系统初始值;另外一次在初始化阶段,赋予程序员定义的初始值。

2. 操作数栈:操作数栈的最大深度也在编译的时候写入到Code属性的max_stacks数据项中。

操作数栈的每一个元素能是任意的Java数据类型,32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。

这里有一点需要注意的是,操作数栈中元素的数据类型必需与字节码指令的序列严格匹配。

为了对栈帧进行优化,虚拟机会让下面栈帧的部分操作数栈与上面栈帧的部分局部变量表重叠在一起,这样在进行方法调使用时即可以共使用一部分数据。

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

常量池中存有大量的符号引使用,字节码中的方法调使用指令就以常量池中指向方法的符号引使用作为参数。

这些符号引使用一部分会在类加载阶段或者者第一次用的时候就转化为直接引使用,这种转化称为静态解析。另外一部分将在每一次运行期间转化为直接引使用,这部分称为动态连接。这块不知道大家对前面说的静态绑定和绑定有没有印象,其实道理都是差不多的。

4. 方法返回地址:当一个方法开始执行后,只有两种方式能退出这个方法。第一种方式是执行引擎遇到任意一个方法返回的字节码指令,如return,这种退出方法的方式称为正常完成出口(Normal Method Invocation Completion)。

另外一种退出方式是,在方法执行过程中遇到了异常,并且这个异常没有在方法体内得四处理,无论是Java虚拟机内部产生的异常,还是代码中用athrow字节码指令产生的异常,只需在本方法的异常表中没有搜索到匹配的异常解决器,就会导致方法退出,这种退出方法的方式称为异常完成出口(Abrupt

Method Invocation Completion)。

无论采使用何种退出方式,在方法退出之后,都需要返回到方法被调使用的位置,程序才可以继续执行;而方法异常退出时,返回地址是要通过异常解决器表来确定的。

方法退出的过程实际上就等同于把当前栈帧出栈,因而退出时可可以执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(假如有的话)压入调使用者栈帧的操作数栈中,调整PC计数器的值以指向方法调使用指令后面的一条指令等。

5. 附加信息:虚拟机规范允许具体的虚拟机实现添加少量规范里没有形容的信息到栈帧之中。

以上即为栈帧的知识点,都是比较概念化的东西,大部分摘自《深入了解Java虚拟机》,这里权且记录,侵删。

喜欢文章或者想一起学习的朋友能关注我,给我点赞,我将会持续升级,有什么疑问或者文中有不当之处请给我留言,真挚地希望可以与大家一起交流讨论,学习进步!

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » 深入了解JAVA虚拟机学习笔记26——字节码执行引擎(栈帧)

发表回复