字节码解释执行引擎

lllllan ... 2022-3-26 Java
  • JVM
  • 深入理解Java虚拟机
About 5 min

转载声明

  • 《深入理解Java虚拟机》

# 一、解释执行

Java语言经常被人们定位为【解释执行】的语言,在Java初生的JDK 1.0时代,这种定义还算是比较准确的,但当主流的虚拟机中都包含了【即时编译器】后,Class文件中的代码到底会被解释执行还是编译执行,就成了只有虚拟机自己才能准确判断的事。

大部分的程序代码转换成物理机的目标代码或虚拟机能执行的指令集 之前,都需要经过图8-4中的各个步骤

image-20220326151852398

在Java语言中,Javac 编译器完成了程序代码经过词法分析、语法分析到抽象语法树,再遍历语法树生成线性的字节码指令流的过程。因为这一部分动作是在Java虚拟机之外进行的,而解释器在虚拟机的内部,所以Java程序的编译就是半独立的实现。

# 二、基于栈的指令集和基于寄存器的指令集

Javac编译器输出的字节码指令流,基本上是一种【基于栈的指令集架构】(Instruction Set Architecture,ISA),字节码指令流里面的指令大部分都是零地址指令,它们依赖操作数栈进行工作。

与之相对的另外一套常用的指令集架构是【基于寄存器的指令集】,最典型的就是x86的二地址指令集,如果说得更通俗一些就是现在我们主流PC机中物理硬件直接支持的指令集架构,这些指令依赖寄 存器进行工作

举个最简单的例子,分别使用这两种指令集去计算【1+1】的结果:

两种指令集各自的优缺

基于栈的指令集:

  • 可以自由地把访问最频繁的数据放到寄存去中获取尽量好的性能
  • 代码相对更加紧凑、编译器实现更加简单
  • 由于指令数量和内存访问的原因,执行速度略慢

基于寄存器的指令集:

  • 不可能避免地要受到硬件的约束
  • 直接依赖寄存及,理论上执行速度更快

# 三、基于栈的解释器执行过程

public int calc() {
    int a = 100;
    int b = 200;
    int c = 300;
    return (a + b) * c;
}
1
2
3
4
5
6
public int calc();
	Code:
		Stack=2, Locals=4, Args_size=1
        0: bipush 100
		2: istore_1
		3: sipush 200
		6: istore_2
		7: sipush 300
		10: istore_3
		11: iload_1
		12: iload_2
		13: iadd
		14: iload_3
		15: imul
		16: ireturn
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 0

首先,执行偏移地址为0的指令,Bipush指令的作用是将单字节的整型常量值(-128~127)推入操作数栈顶,跟随有一个参数,指明推送的常量值,这里是100

image-20220326155149877

# 1

执行偏移地址为2的指令,istore_1 指令的作用是将操作数栈顶的整型值出栈并存放到第1个局部变量槽中。后续4条指令(直到偏移为11的指令为止)都是做一样的事情,也就是在对应代码中把变量 a、b、c赋值为100、200、300。这4条指令的图示略过。

image-20220326155221300

# 2

执行偏移地址为11的指令,iload_1指令的作用是将局部变量表第1个变量槽中的整型值复制到操作数栈顶。

image-20220326155315623

# 3

执行偏移地址为12的指令,iload_2指令的执行过程与iload_1类似,把第2个变量槽的整型值入栈。 画出这个指令的图示主要是为了显示下一条iadd指令执行前的堆栈状况。

image-20220326155344243

# 4

执行偏移地址为13的指令,iadd 指令的作用是将操作数栈中头两个栈顶元素出栈,做整型加法,然后把结果重新入栈。在iadd指令执行完毕后,栈中原有的100和200被出栈,它们的和300被重新入栈。

image-20220326155414066

# 5

执行偏移地址为14的指令,iload_3 指令把存放在第3个局部变量槽中的300入栈到操作数栈中。这时操作数栈为两个整数300。下一条指令imul是将操作数栈中头两个栈顶元素出栈,做整型乘法,然后把结果重新入栈,与iadd完全类似

image-20220326155505863

# 6

执行偏移地址为16的指令,ireturn指令是方法返回指令之一,它将结束方法执行并将操作数栈顶的整型值返回给该方法的调用者。到此为止,这段方法执行结束

image-20220326155630648

Last update: 2022年3月26日 15:58
Contributors: lllllan