1.栈帧
栈帧用于支持虚拟机进行方法调用和方法执行的数据结构,栈帧存储了方法的局部变量表、操作数栈、动态链接、和方法返回地址信息。
1.1 局部变量表
用于存储方法参数和方法内部定义的局部变量。 局部变量表的大小在方法的Code属性中就已经定义好了,为max_locals的值。对于实例方法而言,索引为0的slot存放的是this引用,之后再依次存放方法参数,定义的局部变量;slot可以被重用,当局部变量已经超出了作用域时,在作用域外在定义局部变量时,可以重用之前的slot空间。同时,局部变量没有赋值是不能够使用的,这和类变量和实例变量是有不同的。
1.2操作数栈
执行方法时,存放操作数的栈,栈的深度在方法的Code属性中已经定义好了,为max_stack的值。操作数栈可以与其他栈的局部变量表共享区域,这样可以共用一部分数据,无须进行额外的参数复制传递。
1.3动态链接
每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的过程中的动态连接(Dynamic Linking)。这些符号引用一部分会在类加载阶段或者第一次使用的时候转化为直接引用(静态解析);另一部分将在每一次运行期间转化为直接引用(动态连接)。
1.4方法返回地址
正常方法返回,返回地址为到调用该方法的指令的下一条指令的地址;异常返回,返回地址由异常表确定。方法返回时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC值。
2.方法调用
方法调用阶段唯一任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。
2.1解析
在程序执行前就已经确定了方法调用的版本,即编译期就确定了调用方法版本,这个版本在运行时是不可变的。静态方法、私有方法、实力构造器、父类方法、final方法在编译时就可以确定具体的调用版本,静态方法直接与类型相关、私有方法在外部不可访问、final不可被继承,也可唯一确定,这些方法称为非虚方法,其他方法称为虚方法。
2.2分派
静态分派,动态分派,单分派,多分派。
2.2.1静态分派
与静态分派相关的就是方法的重载,重载时根据参数的静态类型而非实际类型决定调用哪个版本。静态分派发生在编译阶段。
2.2.2动态分派
与动态分派相关的就是方法的重写,在子类中我们会重写父类的方法,而在调用的时候根据实际类型来确定适合的调用版本。
2.2.3单分派与多分派
方法的接受者与方法的参数统称为方法的宗量。单分派根据一个宗量确定调用方法的版本;多分派根据多个宗量确定调用方法。
静态分派过程如下,在编译期阶段,会根据静态类型与参数类型确定调用版本,在编译期,是由多个宗量确定调用版本,是静态多分派。动态分派过程如下,在运行期,方法签名确定,静态类型和实际类型此时都不会对方法本身产生任何影响,而虚拟机会根据实际类型来确定调用版本,只根据一个宗量进行确定,因此,在运行时,是动态单分派。