虚拟机字节码执行引擎
栈帧
栈帧结构
- 局部变量表
- 操作数栈
- 动态连接
- 方法返回地址
- 附加信息
局部变量表
- 变量槽(Slot):每个变量槽应该能存放一个
boolean
,byte
,char
,short
,int
,float
,reference
或returnAddress
类型的数据,Slot的实际长度可以随着处理器,操作系统或虚拟机的不同而变化。对于64位数据类型,long
和double
,存储时将以高位对齐的方式占用两个连续的变量槽。 - 索引定位:虚拟机通过索引定位的方式使用局部变量表,索引值的范围是从0开始至局部变量表最大的Slot数量。
- 参数传递:虚拟机通过使用局部变量表完成参数值到参数变量列表的传递过程,当执行实例方法时,第0位变量槽默认用于传递方法所属对象实例的引用,即”this”。
- 局部变量:参数列表分配完毕后,根据方法体内部定义变量的顺序和作用域分配变量槽。
- 变量槽的复用:当代码执行离开了局部变量A的作用域之后,如果有新的局部变量被定义,那么A使用的变量槽就可以被新的局部变量复用。
- 局部变量的初始化:局部变量未经初始化不能被使用,局部变量不会被自动赋予零值。
操作数栈
- 栈元素:32位数据类型元素占用一个容量,64位数据类型元素占用两个容量。
- 作用:供字节码指令执行的时候,写入和提取内容使用。
动态连接
动态连接指在每一次运行期间将方法的符号引用转化为直接引用的。与之相对应的是静态解析,在类加载阶段或第一次使用时就转化为直接引用。
方法返回地址
- 正常完成出口:执行任意一个方法返回的字节码指令,是否给方法调用者产生返回值由字节码指令决定。
- 异常完成出口:在方法执行过程中遇到了异常,并且异常没有在方法体内得到处理,导致方法退出,不会给方法调用者产生返回值。
- 方法返回地址:一般来说,方法正常退出时,栈帧中的方法返回地址会使用调用者的PC计数器的值,而方法异常退出时,返回地址需要通过异常处理器表来确定。
方法调用
- 非虚方法:指在类加载阶段,可以确定调用版本,并且调用版本在运行期不可变的方法,主要包括主要包括静态方法和私有方法,以及使用final修饰的方法。
- 解析:在类加载阶段,虚拟机会将非虚方法的符号引用转化为直接引用。
- 静态类型:也称为外观类型,指变量的字面值类型或表达式值类型。静态类型变化在编译期是可知的。
- 实际类型:指变量的在运行期时的类型。实际类型变化在编译期是不可知的。
- 静态分派:所有依赖静态类型来确定方法执行版本的分派动作称为静态分派。方法重载是静态分派。静态分派发生在编译期。
- 动态分派:通过实际类型来确定方法执行版本的分派动作称为动态分派。方法重写是动态分派。动态分派发生在运行期。
- 方法的宗量:方法的接受者与方法的参数统称为方法的宗量。
- 单分派与多分派:单分派是根据一个宗量来确定方法执行版本,多分派是根据多个宗量来确定方法执行版本。在Java中,静态分派属于多分派(分派时需要确定方法的接受者和参数),动态分派属于单分派(分派时方法的接受者已确定,只需确定方法的参数)。
动态语言支持
- MethodHandle:JDK 1.7新增的用于动态确定目标方法的机制,类似于C/C++中的函数指针或C#中的委托。
- invokedynamic指令:在字节码层面实现与MethodHandle相似的功能。