目录
当前栈帧结构
栈帧包括:
- 局部变量表
- 操作数栈
- 动态连接
- 方法返回地址
- 附加信息
当前栈帧,执行引擎运行!
1 局部变量表
用于存放方法参数和方法内部定义的局部变量。
以变量槽(variable slot)为最小单位,使用方式,索引定位的方式。:
- 32位数据类型有boolean,byte,char, short,int,float,reference,returnaddress,索引n代表使用第n个slot
- 64位数类型有long,double.同时使用n和n+1两个slot.
注意:局部变量定义后,必须赋初值。
2 操作数栈
是一种后入先出栈,深度不超过max_stacks数据项中设定的最大值。
栈容量:32位占1,64位占2.
java虚拟机的解释执行引擎称为“基于栈的执行引擎”,这里的栈就是操作数栈。
3 动态连接
class文件存在大量的符号引用,分为两种:
- 类加载阶段,或者第一次使用时转化为直接引用,称为静态解析。
- 每一次运行期间转化为直接引用,称为动态连接。
4 方法返回地址
两种返回方法:
- 正常完成出口,有方法返回的字节码指令。此时会给上层产生返回值,返回地址为pc计数器的值。
- 异常完成出口,执行中遇到异常,并且异常没有在方法体中得到处理。此时不会给上层产生返回值,返回地址由异常处理器确定。
退出步骤如下?
- 恢复上层方法的局部变量表和操作数栈,
- 把返回值压入调用者栈帧的操作数栈中,
- 调整pc计数器的值以指向方法调用指令后面的一条指令。
5 附加信息
允许虚拟机实现增加一些规范中没有描述的信息。
方法调用
解析
即直接引用,编译期可知,运行期不可变。主要包括静态方法和私有方法两大类。
5条方法调用字节码指令:
- invokestatic:调用静态方法,分配逻辑由JAVA虚拟机决定。
- invokespecial:调用实例构造器<init>方法,私有方法和父类方法,分配逻辑由JAVA虚拟机决定。
- invokevirtual:调用虚方法,分配逻辑由JAVA虚拟机决定。
- invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象,分配逻辑由JAVA虚拟机决定。
- invokedynamic:运行时动态解析调用点限定符所用的方法,然后执行该方法,这里的分配逻辑由用户设定的引导方法决定。
方法分类
- 非虚方法:能被invokestatic和invokespecial指令调用的方法,可以在解析阶段确定唯一版本,包括静态方法,私有方法、实例构造器、父类方法四种。
- 虚方法:被invokevirtual、invokeinterface和invokedynamic指令调用的方法,这些方法只有在运行阶段才能确定。
注意:final修饰的方法,也是一种非虚方法。
分派
1,静态分派
静态分派与重载有关,发生在编译期。示例如下:
package demo.jvm.diarys;
/**********************
*
* @author daiyi
*
*/
public class StaticDispatch_8_6 {
public static abstract class Human{
}
public static class Man extends Human {
}
public static class Woman extends Human {
}
public void sayHello(Human people) {
System.out.println("hello Human");
}
public void sayHello(Man people) {
System.out.println("hello Man");
}
public void sayHello(Woman people) {
System.out.println("hello Woman");
}
public static void main(String[] args) {
// TODO 自动生成的方法存根
Human man = new Man();
Human women = new Woman();
StaticDispatch_8_6 sd = new StaticDispatch_8_6();
sd.sayHello(man);
sd.sayHello(women);
}
}
运行结果如下:
hello Human
hello Human
虚拟机重载时,时通过参数的静态类型而不是实际类型作为判定依据的,且静态类型是编译期可知的。
2 动态分派
动态分派与重写有关,发生在运行期。示例如下:
package demo.jvm.diarys;
/**********************
*
* @author diarys
* test static dispath and dynamic dispath
*/
public class DynamicDispath_8_8 {
/*static dispatch*/
static class Animal{
}
static class Monkey extends Animal{
}
static class Lion extends Animal {
}
/*dynamic dispatch*/
static abstract class Human{
protected abstract void sayHello(Animal animal) ;
protected abstract void sayHello(Monkey monkey) ;
protected abstract void sayHello(Lion lion) ;
}
static class Man extends Human{
protected void sayHello(Animal animal) {
System.out.println("man sayHello to animal");
}
protected void sayHello(Monkey monkey) {
System.out.println("man sayHello to monkey!");
}
protected void sayHello(Lion lion) {
System.out.println("man sayHello to lion!");
}
}
static class Woman extends Human{
protected void sayHello(Animal animal) {
System.out.println("woman sayHello to animal");
}
protected void sayHello(Monkey monkey) {
System.out.println("woman sayHello to monkey!");
}
protected void sayHello(Lion lion) {
System.out.println("woman sayHello to lion!");
}
}
/*demo
* man sayHello to animal
man sayHello to monkey!
woman sayHello to animal
woman sayHello to monkey!
woman sayHello to animal
woman sayHello to monkey!
*
* */
public static void main(String[] args) {
// TODO 自动生成的方法存根
Human man = new Man();
Human woman = new Woman();
Animal animal = new Animal();
Animal monkey = new Monkey();
Animal lion = new Lion();
man.sayHello(animal);
man.sayHello((Animal)animal);
man.sayHello(monkey);
man.sayHello((Monkey)monkey);
man.sayHello(lion);
man.sayHello((Lion)lion);
woman.sayHello(monkey);
woman.sayHello((Monkey)monkey);
//变换man的实际类型为Woman
man = new Woman();
//获取Woman的sayhello,参数是
man.sayHello(monkey);
man.sayHello((Monkey)monkey);
}
}
运行结果如下:
man sayHello to animal
man sayHello to monkey!
man sayHello to animal
man sayHello to lion!
woman sayHello to animal
woman sayHello to monkey!
woman sayHello to animal
woman sayHello to monkey!
3 单分派和多分派
分派(Dispath)可以是静态的也可以是动态的,根据分配依据的宗量数,又可分为单分派和多分派。两两结合,便得到:静态单分派、静态多分派、动态单分派、动态多分派。
JAVA1.7 属于静态多分派,动态单分派。
4 动态分派的实现