JVM 字节码执行引擎
本文将介绍 JVM 中的字节码执行引擎。
一、什么是字节码执行引擎?
字节码执行引擎是 JVM 的核心组成部分之一,其任务是 读取字节码并执行,是 “一次编写,处处运行” 这一特性的支撑。
二、运行时栈帧结构
具体请看:
三、方法调用
1. 什么是方法调用?
方法调用,其实更应该叫做 “方法确定”,即 确定要被调用的具体方法,而不是执行方法中的代码。
2. 解析调用
解析调用指的是对于 “编译期可知,运行期不可变” 的方法的确定。这类方法包括:静态方法、私有方法、final 方法、构造方法、父类方法,它们的具体方法在编译期就可以确认下来,并且不可能通过继承或别的方式重写出其它版本。
3. 重载解析调用
重载解析,又被称作 “静态分派”,两种说法均可。
重载解析调用指的是对于重载方法的确定。Java 提供了重载机制,允许定义若干个名字相同但参数不同的方法,待调用时再根据传入参数的数量及类型,自动选择对应的方法执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
abstract class Animal {
}
abstract class Dog extend Animal {
}
abstract class Cat extend Animal {
}
void sayHello(Animal animal) {
System.out.println("动物")
}
void sayHello(Dog dog) {
System.out.println("狗")
}
void sayHello(Cat cat) {
System.out.println("猫")
}
Animal dog = new Dog();
Animal cat = new Cat();
Dog dog1 = new Dog();
// 动物
sayHello(dog);
// 动物
sayHello(cat);
// 狗
sayHello(dog1);重载方法的确认是根据参数的数量和 “定义类型” 进行判断的,而这两者都可以在编译时确认,因此重载方法也可以在编译时确认。
4. 分派
分派调用指的是对于重写方法的确定。Java 提供了重写机制,允许子类重写父类的方法。
重写方法的确定需要根据调用者的 “具体类型” 进行判断,而 “具体类型” 只有运行时才可以确认,因此只能在执行时根据 “具体类型” 来分派方法执行版本。
确认步骤大致为:
- 确认调用对象的 “具体类型”,记作 C
- 在 C 中寻找对应方法
- 若未找到,按照继承关系逐级寻找
确认步骤具体落实到虚拟机上时,基于性能的考虑,虚拟机往往不会采用逐级向上搜索的方式。一种常见的做法是为类型在方法区中建立一个虚方法表,表中存放着类型的各个方法的实际引用。虚方法表往往在类加载的连接阶段初始化完成。通过虚方法表,对方法的确定不再需要逐级搜索,只需要查表即可。
四、字节码执行
通过方法调用确定的要调用的方法后,接下来就应该执行其中的字节码。
字节码的执行由 基于栈的字节码解释执行引擎 完成。
其中,
基于栈:指令集架构有基于栈的指令集架构、基于寄存器的指令集架构,Java 编译器输出的字节码是基于栈的
解释执行:Java 语言常被定义为解释执行,但是 JVM 演变到如今,许多虚拟机都包含了即时编译器,代码既可能被解释执行,也可能被编译执行
五、动态类型语言支持
1. 动态类型语言
动态语言的关键特征是:类型检查在运行期进行,而非在编译期。
典型的静态类型语言是 Java,典型的动态类型语言是 JavaScript。
2. 具体
略。
参考
- 深入理解 Java 虚拟机