bilibili-JVM学习笔记16
The Java Virtual Machine Specification - Java SE 8 Edition
JVM学习笔记11 - Java字节码初识JVM学习笔记12 - 解读笔记11中的attributesJVM学习笔记13JVM学习笔记14 异常JVM学习笔记15 方法执行
编译执行 解释执行
- 现代 JVM 在执行 Java 代码的时候,通常都会将解释执行与编译执行二者结合起来进行;
解释执行
- 通过 jvm 解释器来读取字节码,遇到相应的字节码指令就去执行该指令;
编译执行
- 通过
即时编译器
(Just In Time,JIT
)将字节码指令转换为本地机器码
来执行;现代 JVM 会根据代码热点
来生成相应的本地机器码; - 失去了可移植性的优点
- 提高了执行效率
- 基于栈的指令集与基于寄存器的指令集之间的关系
- JVM 执行字节码指令所采取的方式是基于栈的指令集
- 主要操作有入栈和出栈;
- 优点:可以在不同平台之间移植
- 缺点:完成相同的操作,指令数量通常比基于寄存器的指令集数量要多;在内存中完成执行指令的操作,速度慢很多;
- 虽然虚拟机可以采用一些优化手段,但总体来说,基于栈的指令集的执行还是要慢一些的;
- 基于
寄存器
的指令集
- 与硬件架构紧密关联,无法做到可移植;
- 基于寄存器的指令集是直接由
CPU
来执行的,它是在高速缓冲区
中进行执行的,速度比在内存中执行高出一个数量级;
JVM执行栈指令集实例剖析
java 源码
package new_package.jvm.p55;
public class MyTest {
public int cal() {
int a = 6;
int b = 1;
int c = 5;
int d = 3;
int result = (a + b - c) * d;
return result;
}
public static void main(String[] args) {
System.out.println(new MyTest().cal());
}
}
javap -v new_package.jvm.p55.MyTest
cal 方法
相关的字节码
public int cal();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=6, args_size=1
0: bipush 6
2: istore_1
3: iconst_1
4: istore_2
5: iconst_5
6: istore_3
7: iconst_3
8: istore 4
10: iload_1
11: iload_2
12: iadd
13: iload_3
14: isub
15: iload 4
17: imul
18: istore 5
20: iload 5
22: ireturn
LineNumberTable:
line 6: 0
line 7: 3
line 8: 5
line 9: 7
line 10: 10
line 11: 20
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 this Lnew_package/jvm/p55/MyTest;
3 20 1 a I
5 18 2 b I
7 16 3 c I
10 13 4 d I
20 3 5 result I
解析:
- stack=2 表示当前方法的
操作数栈
最大深度为 2 - locals=6 表示当前方法
局部变量表
长度为 6 - args_size=1 表示当前方法入参数量为 1 ;(
实例方法
的第一个隐形入参this
,指向当前实例)
接下来对字节码指令逐个解释:
- bipush 6
bipush :将一个 int 类型的数值压入操作数栈
此处是将数值 6
压入操作数栈
2. istore_1
istore_1 : istore 1 的简写;
将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 1
的位置处
iconst_1 : bipush 1 的简写
此处是将数值 1
压入操作数栈
- istore_2
将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 2
的位置处
- iconst_5
此处是将数值 5
压入操作数栈
- istore_3
将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 3
的位置处
- iconst_3
此处是将数值 3
压入操作数栈
- istore 4
将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 4
的位置处
iload_1 : iload 1 的简写
iload 从局部变量中加载一个 int 类型的值
iload_1 即从索引 1 的位置加载一个值,压入操作数栈
- iload_2
iload_2 即从索引 2 的位置加载一个值,压入操作数栈
从操作数栈中弹出两个值(int 类型的值),然后相加,将结果压入操作数栈
- iload_3
从索引 3 的位置加载一个值,压入操作数栈
从操作数栈中弹出两个数(int 类型),第一个为减数,第二个为被减数,结果为被减数 - 减数,将结果重新压入操作数栈;
- iload 4
从索引 4 的位置加载一个值,压入操作数栈
两个 int 类型的值相乘,并将结果重新压入操作数栈;
- istore 5
将操作数栈顶的 int 类型数值从操作数栈中弹出,放到局部变量表的索引为 5
的位置处
- iload 5
从索引 5 的位置加载一个值,压入操作数栈
从当前帧的操作数栈中弹出值,并将其压入调用者的帧
的操作数栈
。