java虚拟机——栈帧结构

1.总述栈帧

java虚拟机以方法作为基本的执行单元。这个执行单元的数据结构就是 虚拟机栈 中的 栈元素——栈帧

栈帧的结构如下:

如上图所示,栈帧存储了方法的局部变量表,操作数栈,动态连接、方法返回地址和一些额外的附加信息。

对于虚拟机的执行引擎来说,在活动线程中,只有位于栈顶的栈帧才是生效的,即只有当前栈帧是生效的,与当前栈帧关联的方法叫当前方法。执行引擎所运行的所有字节码指令都只针对当前栈帧进行操作。

在编译java源码时,栈帧中需要多大的局部变链表,操作数栈的深度这些已经被分析出来并写到了方法表的Code属性中了,如下图是一个main方法的栈帧中的局部变量表大小等数据:

2.局部变量表

局部变量表中存放方法的局部变量。java源码被编译为Class文件时,某个方法的局部变量表的大小就已经确定在了该方法Code的属性中了 。

局部变量表的容量以变量槽 variable slot 为最小单位。

java虚拟机规范规定,变量槽能存放下一个byte,int ,char,reference,等8种数据类型。这8种数据类型除过reference都是没有超过32位的。reference是一个对象的引用,虚拟机没有明确规定其长度,其是32还是64位取决于虚拟机本身是32位还是64位的。

java虚拟机中明确规定的64位长度的数据类型只有long和double,当存储这个类型的变量时,会分配2个连续的变量槽来存储。由于局部变量表是虚拟机栈中的,是线程的私有变量,因此无论读写2个连续的变量槽是否是原子操作,都不会引起数据竞争和线程安全问题。

对于使用2个相邻的变量槽来存在一个64位数据,java虚拟机不允许以任何方式单独访问其中的一个变量槽,必须2个一起访问。

局部变量和之前的类变量有一个很大的差异:

类的字段变量有2次赋初值的过程:准备阶段会赋初值,初始化阶段,赋予程序代码定义的初始值。因此,在写代码时,即使没有为类变量赋初值也是可以使用的,因为在准备阶段会自动为其赋一个初值。

局部变量如果在程序里没有赋值,则是完全不能使用的。

3.操作数栈

顾名思义,操作数栈是一个后进先出的栈结构。同局部变量表一样,栈的最大深度也在编译的时候就写入了方法的Code属性里了。在方法执行的任何时候,栈的深度都不会超过这个最大值。操作数栈中的元素可以是任意的java数据类型。其中,32位数据类型所占的栈容量是1,64位操作数栈所占的栈容量是2. 计算的时候,会将数据出栈,计算完后,再将结果入栈。所计算的数据类型必须严格与字节码的序列匹配。比如iadd操作时,最接近栈顶的两个元素必须是int类型。

4.动态连接

字节码文件中的常量池中存在大量的符号引用,字节码中的方法调用就以常量池中的符号引用作为参数。这些符号引用一部分会在类加载或第一次使用的时候被转换为直接引用,这种转换称为静态解析。另外一部分会在每一次运行期间都转换为直接引用,这部分就称为动态连接。栈帧中的动态连接,就是一个指向运行时常量池中该栈帧所属方法的引用,该引用就是为了支持方法调用过程中的动态连接。

5.方法返回地址

一个方法开始执行中,只有2种方式可以退出这个方法。一种是正常退出,即执行引擎碰到任意一个方法返回的字节码指令,就会将返回值(如果有返回值)返回给上层调用者。另外一种是 异常调用完成。异常退出的方法是不会给上层调用者返回任何返回值的。

方法的退出就对应着虚拟机栈中的栈帧出栈。调用对应着入栈。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值