笔记:《深入理解Java虚拟机》第八章-虚拟机字节码执行引擎

虚拟机字节码执行引擎

1 运行时栈帧结构

栈帧存储了方法的局部变量表操作数栈动态连接返回地址等信息。每一个方法从开始执行到执行结束,对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
只有位于栈顶的栈帧才是有效的,称为当前栈帧,与这个栈帧所对应的方法称为当前方法。执行引擎执行的所有字节码只对当前栈帧进行操作,典型的栈帧结构如图1.1
在这里插入图片描述

图1.1 栈帧的概念结构

1.1 局部变量表

局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。
NB. 局部变量定义却没有赋初始值是不可以使用的,不管在编译阶段还是字节码校验阶段。

1.2 操作数栈

操作数栈是一个后入先出栈,其中的元素可以是任何Java数据类型,64位数据类型所占栈容量为2,32位数据类型所占栈容量为1。在方法执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是入栈/出栈操作。
NB. Java虚拟机的解释执行引擎称为“面向栈的执行引擎”,这里“栈”即为操作数栈。

1.3 动态连接

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有该引用的目的是支持方法调用过程中的动态连接。

符号引用转为直接引用的方式:

  • 在类加载阶段或第一次使用的时就转化为直接引用,称为静态解析
  • 在每一次运行期间转化成直接引用的称为动态连接

1.4 方法返回地址

当方法开始执行后,有两种方法退出这个方法:

  • 正常完成出后:当执行引擎遇到一个方法返回的字节码指令,这时可能会有返回值传递给上层调用者。
  • 异常完成出口:当方法执行过程中遇到了异常,并且这个异常没有在本方法中得到处理,这时不会有返回值传递给上层调用者。

无论何种方式退出,都会返回到方法被调用的位置。

2 方法调用

2.1 解析

解析:在类加载的解析阶段,将一部分符号引用转化成直接引用。解析成立的条件是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个调用版本在运行期间是不可变的。
NB. Java中满足这个要求主要包括静态方法和私有方法,前者与类直接相关、后者在外部不可访问。

2.2 分派

2.2.1 静态分派

静态分派:所有依赖静态类型来定位方法执行版本的分派动作。
方法静态分派演示:

public class StaticDispatch {
	static  abstract class Human{
		
	}
	static class Man extends Human{
		
	}
	static class Woman extends Human{
		
	}
	public void sayHello(Human human) {
		System.out.println("hello guy");
	}
	public void sayHello(Man man) {
		System.out.println("hello gentleman");
	}
	public void sayHello(Woman woman) {
		System.out.println("hello lady");
	}
	public static void main(String[] args) {
		Human man = new Man();
		Human woman = new Woman();
		StaticDispatch dispatch = new StaticDispatch();
		dispatch.sayHello(man);
		dispatch.sayHello(woman);
	}
}

/*
	输出:
	hello guy
	hello guy
*/

虚拟机(准确说是编译期)在重载时是通过参数的静态类型而不是实际类型来判定的。并且静态类型在编译期可知,所以在编译阶段,Javac编译器会根据参数的静态类型来决定只用哪一个重载版本。
而对字面量无静态类型,所有会有重载优先级,比如:'a’的重载优先级是:char > int > long > float > double > Character(其装箱类型) > Serializable(装箱类型实现的接口) > Object(其父类,继承层级越高优先级越低) > char…(其变长类型)

import java.io.Serializable;

public class Overlad {
	
	public static void sayHello(Object arg) {
		System.out.println("hello Object");
	}
	public static void sayHello(int arg) {
		System.out.println("hello int");
	}
	public static void sayHello(long arg) {
		System.out.println("hello long");
	}
	public static void sayHello(Character arg) {
		System.out.println("hello Character");
	}
	public static void sayHello(char arg) {
		System.out.println("hello char");
	}
	public static void sayHello(char... arg) {
		System.out.println("hello char...");
	}
	public static void sayHello(Serializable arg) {
		System.out.println("hello Serializable");
	}
	
	public static void main(String[] args) {
		sayHello('a');
	}
}
2.2.2 动态分派

动态分派:在运行期间根据实际类型确定方法的执行版本的分派过程。

public class DynamicDispatch {
	static  abstract class Human{
		public abstract void sayHello();
	}
	static class Man extends Human{
		public void sayHello() {
			System.out.println("hello gentleman");
		}
	}
	static class Woman extends Human{
		public void sayHello() {
			System.out.println("hello lady");
		}
	}
	
	public static void main(String[] args) {
		Human man = new Man();
		Human woman = new Woman();
		
		man.sayHello();
		woman.sayHello();
		man = new Woman();
		man.sayHello();
	}
}
/*
	输出:
	hello gentleman
	hello lady
	hello lady
*/
2.2.3 单分派与多分派

根据一个宗量对目标方法进行选择是单分派;根据多于一个宗量对目标方法进行选择是多分派
宗量:方法的接受者与方法的参数。
Java是一门静态多分派、动态单分派语言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值