虚拟机字节码执行引擎

栈帧

栈帧结构

  1. 局部变量表
  2. 操作数栈
  3. 动态连接
  4. 方法返回地址
  5. 附加信息

局部变量表

  1. 变量槽(Slot):每个变量槽应该能存放一个boolean, byte, char, short, int, float, referencereturnAddress类型的数据,Slot的实际长度可以随着处理器,操作系统或虚拟机的不同而变化。对于64位数据类型,longdouble,存储时将以高位对齐的方式占用两个连续的变量槽。
  2. 索引定位:虚拟机通过索引定位的方式使用局部变量表,索引值的范围是从0开始至局部变量表最大的Slot数量。
  3. 参数传递:虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程,当执行实例方法时,第0位变量槽默认用于传递方法所属对象实例的引用,即"this"。
  4. 局部变量:参数列表分配完毕后,根据方法体内部定义变量的顺序和作用域分配变量槽。
  5. 变量槽的复用:当代码执行离开了局部变量A的作用域之后,如果有新的局部变量被定义,那么A使用的变量槽就可以被新的局部变量复用。
  6. 局部变量的初始化:局部变量未经初始化不能被使用,局部变量不会被自动赋予零值。

操作数栈

  1. 栈元素:32位数据类型元素占用一个容量,64位数据类型元素占用两个容量。
  2. 作用:供字节码指令执行的时候,写入和提取内容使用。

动态连接

动态连接指在每一次运行期间将方法的符号引用转化为直接引用的。与之相对应的是静态解析,在类加载阶段或第一次使用时就转化为直接引用。

方法返回地址

  1. 正常完成出口:执行任意一个方法返回的字节码指令,是否给方法调用者产生返回值由字节码指令决定。
  2. 异常完成出口:在方法执行过程中遇到了异常,并且异常没有在方法体内得到处理,导致方法退出,不会给方法调用者产生返回值。
  3. 方法返回地址:一般来说,方法正常退出时,栈帧中的方法返回地址会使用调用者的PC计数器的值,而方法异常退出时,返回地址需要通过异常处理器表来确定。

方法调用

  1. 非虚方法:指在类加载阶段,可以确定调用版本,并且调用版本在运行期不可变的方法,主要包括主要包括静态方法和私有方法,以及使用final修饰的方法
  2. 解析:在类加载阶段,虚拟机会将非虚方法的符号引用转化为直接引用。
  3. 静态类型:也称为外观类型,指变量的字面值类型或表达式值类型。静态类型变化在编译期是可知的。
  4. 实际类型:指变量的在运行期时的类型。实际类型变化在编译期是不可知的。
  5. 静态分派:所有依赖静态类型来确定方法执行版本的分派动作称为静态分派。方法重载是静态分派。静态分派发生在编译期。
  6. 动态分派:通过实际类型来确定方法执行版本的分派动作称为动态分派。方法重写是动态分派。动态分派发生在运行期。
  7. 方法的宗量:方法的接受者与方法的参数统称为方法的宗量。
  8. 单分派与多分派:单分派是根据一个宗量来确定方法执行版本,多分派是根据多个宗量来确定方法执行版本。在Java中,静态分派属于多分派(分派时需要确定方法的接受者和参数),动态分派属于单分派(分派时方法的接受者已确定,只需确定方法的参数)。

动态语言支持

  1. MethodHandle:JDK 1.7新增的用于动态确定目标方法的机制,类似于C/C++中的函数指针或C#中的委托。
  2. invokedynamic指令:在字节码层面实现与MethodHandle相似的功能。