栈帧的内存结构

101 阅读4分钟

栈帧的内存结构通常情况下分成4类,分别是,局部变量表、操作数栈、动态链接、返回地址

1. 局部变量表

这个局部变量表主要存储的是,方法的形参,以及方法内部的各种参数以及最终的返回值,这个局部变量表是存储在当前的栈帧中的,不会出现线程安全的问题。在这个局部变量表中,是以slot为单位进行存储的。通常情况下32位的数据类型占据1个slot,64位的数据类型占据2个slot。与此同时,在这个局部变量表中记录了这个局部变量开始生效到结束生效的范围。以字节码指令的行号表示。特别注意,this也是一个变量,并且这个this只有在实例方法的时候才存在,在静态方法中不存在。为了高效利用存储空间,局部变量表的空间是可以持续利用的,因为在栈帧中,可能某些局部变量就会销毁。其实也可以这样理解这个问题,因为局部变量表是已经确定好了的,如果一个对象在使用期间被销毁,那么显然,总不能销毁这个空间。因此,需要重复利用这个局部变量表。

2.操作数栈

操作数栈,这个结构也是一个由数组构成的一个结构,为什么不是栈呢?可以这样认为,因为JVM已经是偏底层的结构啦,不太能用高级的数据结构做一些操作。在数组中,一旦被创建,这个数组的长度应该就是确定的,在操作数栈中用栈深度来确定,max_stack表示栈深度。类似于局部变量表,32位类型数据占据1个栈深度,64位占据两个栈深度。操作数栈通常是用来执行栈帧中的布局变量的运行过程,在中间进行一系列的存储,运算,以及一些赋值操作。如果这个方法有返回值的话,这个返回值一定要进行入栈操作。因为在后面调用这个方法的位置需要拿出来这个操作数。

3.动态链接

动态链接指的是在栈帧内部,一个指向运行时常量池方法的一个引用地址。这个动态链接的作用其实是,在栈帧运行过程中需要调用方法的时候,这个方法到底指向哪一个方法,这个确认过程就是通过动态链接实现的。
实际字节码指的是 invokevirtual #7 //Method methodA()
在这个位置是需要把符号引用转为运行时的直接引用。因为实际运行的方法一般是存储在运行时常量池的,如果要调用,则需要指向这个实际位置。

方法重写的本质其实就是先在当前类中找,看有没有描述符和调用的方法都相符的方法,有的话查看权限,没有的话向上一层继续调用。

3.1动态绑定和静态绑定

动态绑定只能在程序运行的时候才能确认下来要调用的方法到底是哪一个
静态绑定指的是:这个符号引用调用的方法在编译的时候就可以确定下来。

  • invokevirtual

  • invokeinterfence

  • invokespecial

  • invokestatic

3.2虚方法非虚方法

虚方法:非虚方法以外的都是虚方法。
非虚方法:在编译期间就可以确定的方法,例如final修饰的方法、私有方法、静态方法、构造方法

3.3 动态类型

在Java中的invokedynameic指明的就是当前方法为一个动态类型确定的方法。这里主要借鉴于动态类型语言和静态类型语言的区别。仅仅当变量传入的时候,并且运行起来才能确定这个这个变量的类型。这里的invokedynamic主要指的就是lambda表达式是一个函数式的接口,这个类型是Func类型。仅仅当lambda表达式传入的时候才能确定这个参数的具体类型。

3.4 虚方法表

主要就是建立一个表,以避免遇到虚方法的时候不断去寻找。主要原因如第三节概述方法重写的本质其实就是先在当前类中找,看有没有描述符和调用的方法都相符的方法,有的话查看权限,没有的话向上一层继续调用。

4.返回地址

这个参数存储的是调用本方法的PC寄存器的值,原因是当方法结束后,需要回到调用该方法的位置继续执行。方法的返回指令呢,可以分为如下类型

  • return
  • ireturn
  • lreturn
  • areturn
  • dreturn
    但是,如果没有正常退出的时候,需要按照异常处理表执行异常发生时的流程。