第8章 虚拟机字节码执行引擎
8.2 运行时栈帧结构
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构。
每一个栈帧包括了局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。
在活动线程中,只有位于栈顶的栈帧才是有效的,称为当前栈帧 Current Stack Frame,与这个栈帧相关联的方法称为当前方法。
局部变量表:
Local Variable Table是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
在Java程序编译为Class文件时,就在方法的Code属性中确定了该方法所需要分配的局部变量表的最大容量。
局部变量表的容量以变量槽(slot)为最小单位。
在方法执行时,虚拟机是使用局部变量表完成参数值到参数变量列表的传递过程。
如果执行的是实例方法(非static的方法),那局部变量表中第0位索引的Slot默认用于传递方法所属对象实例的引用,在方法中可以通过关键字“this”来访问这个隐含的参数,其余参数则按照参数顺序排列,占用从1开始的局部变量slot.
操作数栈:
Operand Stack也常称为操作栈,它是一个后入先出(Last In First Out, LIFO)栈。
当一个方法刚刚开始 执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是入栈、出栈操作。
例:整数加法的字节码指令iadd在运行时操作数栈中最接近栈顶的两个元素已经存入了两个int型的数据,当执行这个指令时,会将这两个int值出栈并相加,然后将相加的结果入栈。
动态连接:
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接.
方法返回地址:
当一个方法开始执行后,只有两上方式可以退加该方法:
1 执行引擎遇到任意一个方法返回的字节码指令,正常退出
2 在方法执行过程中出现异常,并且这个那异常没有在方法体内得到处理。
附加信息:
8.3方法调用
方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法)
解析:
所有方法调用中的目标方法在Class里面都是一个常量池中的符号引用,会将其中的一部分符号引用转化为直接引用。主要包括静态方法和私有方法两大类。
分派:
分派调用过程将为揭示多态特征的一些最基本的体现。
1、静态分派
Human man = new Man();
虚拟机在重载时是通过参数的静态类型(Human)而不是实际类型( Man )作为判定依剧的。
虚拟机会根据类型进行自动类型转换或装箱,可变长参数的重载优先级是最低的。
2、动态分派
与多态性的另一个重要体现 重写 override 有着很密切的关联。在判断是调用父类中的方法还是子类中的覆盖的方法时,根据对父类实例化的子类的不同,调用不同子类中覆写的方法。
3、单分派与多分派
方法的接收者与方法的参数统称为方法的宗量。单分派是根据一个宗量对目标方法进行选择,多分派则是根据多于一个宗量对目标方法进行选择。
Java1.6是一门静态多分派、动态单分派的语言。
4、虚拟机动态分派的实现
由于动态分派是非常频繁的动作,因此为类的方法区中建立一个虚方法表。
虚方法表中存放着各个方法的实际入口地址,如果子类没有重写父类的方法,那么入口是一致的。
如果子类重写了,那么子类方法中的地址将会替换为子类的实现版本的入口地址。