字节码指令
指令入门
构造方法字节码指令
public cn.itcast.jvm.t5.HelloWorld();
构造方法的字节码指令 : 2a b7 00 01 b1
-
2a
=> aload_0 加载 slot 0 的局部变量,即 this,做为下面的 invokespecial 构造方法调用的参数 -
b7 => invokespecial 预备调用构造方法 ==> #1
-
00 01 引用常量池中 #1 项,即
【 Method java/lang/Object."<init>":()V 】
-
b1 表示返回
主方法的字节码指令
public static void main(java.lang.String[]);
主方法的字节码指令 : b2 00 02 12 03 b6 00 04 b1
-
b2 => getstatic 用来加载静态变量 ==> #2
-
00 02 ==> 引用常量池中 #2 项,即【Field java/lang/System.out:Ljava/io/PrintStream;】
-
12 => ldc 加载参数 ==> #3
-
03 引用常量池中 #3 项,即 【String hello world】
-
b6 => invokevirtual 预备调用成员方法 ==> #4
-
00 04 引用常量池中 #4 项,即【Method java/io/PrintStream.println:(Ljava/lang/String;)V】
-
b1 表示返回
参考 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5
javap 工具
自己分析类文件结构太麻烦了,Oracle 提供了 javap 工具来反编译 class 文件 ;
$ javap -v HelloWorld.class
Classfile /F:/JavaCode/hm-jvm-codes/jvm-chapter-03/target/classes/cn/knightzz/example/HelloWorld.class
Last modified 2022-10-14; size 573 bytes
MD5 checksum 98ee61ec018245a2c46adf53ab20f1c0
Compiled from "HelloWorld.java"
public class cn.knightzz.example.HelloWorld
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #23 // hello world
#4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #26 // cn/knightzz/example/HelloWorld
#6 = Class #27 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcn/knightzz/example/HelloWorld;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 SourceFile
#19 = Utf8 HelloWorld.java
#20 = NameAndType #7:#8 // "<init>":()V
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#23 = Utf8 hello world
#24 = Class #31 // java/io/PrintStream
#25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V
#26 = Utf8 cn/knightzz/example/HelloWorld
#27 = Utf8 java/lang/Object
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Utf8 java/io/PrintStream
#32 = Utf8 println
#33 = Utf8 (Ljava/lang/String;)V
{
public cn.knightzz.example.HelloWorld();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello world
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 16: 0
line 17: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
}
SourceFile: "HelloWorld.java"
图解方法执行流程
1.原始 java 代码
package cn.knightzz.jvm.bytecode;
/**
* @author 王天赐
* @title: Demo01
* @projectName hm-jvm-codes
* @description: 图解方法执行流程 演示 字节码指令 和 操作数栈、常量池的关系
* @website <a href="http://knightzz.cn/">http://knightzz.cn/</a>
* @github <a href="https://github.com/knightzz1998">https://github.com/knightzz1998</a>
* @create: 2022-10-17 11:20
*/
public class Demo01 {
public static void main(String[] args) {
int a = 10;
int b = Short.MAX_VALUE + 1;
int c = a + b;
System.out.println(c);
}
}
2.编译后的字节码文件
knight'z'z@DESKTOP-VAQG1TR MINGW64 /f/JavaCode/hm-jvm-codes/jvm-chapter-03/target/classes/cn/knightzz/jvm/bytecode (master)
$ javap -v Demo01.class
Classfile /F:/JavaCode/hm-jvm-codes/jvm-chapter-03/target/classes/cn/knightzz/jvm/bytecode/Demo01.class
Last modified 2022-10-17; size 630 bytes
MD5 checksum dceb6841cee3ad7d4b0670a15c198398
Compiled from "Demo01.java"
public class cn.knightzz.jvm.bytecode.Demo01
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#25 // java/lang/Object."<init>":()V
#2 = Class #26 // java/lang/Short
#3 = Integer 32768
#4 = Fieldref #27.#28 // java/lang/System.out:Ljava/io/PrintStream;
#5 = Methodref #29.#30 // java/io/PrintStream.println:(I)V
#6 = Class #31 // cn/knightzz/jvm/bytecode/Demo01
#7 = Class #32 // java/lang/Object
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Lcn/knightzz/jvm/bytecode/Demo01;
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 args
#18 = Utf8 [Ljava/lang/String;
#19 = Utf8 a
#20 = Utf8 I
#21 = Utf8 b
#22 = Utf8 c
#23 = Utf8 SourceFile
#24 = Utf8 Demo01.java
#25 = NameAndType #8:#9 // "<init>":()V
#26 = Utf8 java/lang/Short
#27 = Class #33 // java/lang/System
#28 = NameAndType #34:#35 // out:Ljava/io/PrintStream;
#29 = Class #36 // java/io/PrintStream
#30 = NameAndType #37:#38 // println:(I)V
#31 = Utf8 cn/knightzz/jvm/bytecode/Demo01
#32 = Utf8 java/lang/Object
#33 = Utf8 java/lang/System
#34 = Utf8 out
#35 = Utf8 Ljava/io/PrintStream;
#36 = Utf8 java/io/PrintStream
#37 = Utf8 println
#38 = Utf8 (I)V
{
public cn.knightzz.jvm.bytecode.Demo01();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 12: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/knightzz/jvm/bytecode/Demo01;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: bipush 10
2: istore_1
3: ldc #3 // int 32768
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
13: iload_3
14: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
17: return
LineNumberTable:
line 15: 0
line 16: 3
line 17: 6
line 18: 10
line 19: 17
LocalVariableTable:
Start Length Slot Name Signature
0 18 0 args [Ljava/lang/String;
3 15 1 a I
6 12 2 b I
10 8 3 c I
}
SourceFile: "Demo01.java"
3.常量池载入运行时常量池
![image-20221017144544095](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017144544095.png)
4.方法字节码载入方法区
![image-20221017145203706](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017145203706.png)
5.main 线程开始运行,分配栈帧内存
(stack=2,locals=4)
![image-20221017154721029](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017154721029.png)
6.执行引擎开始执行字节码
bipush 10
bipush 10
==> 将一个 byte 压入操作数栈(其长度会补齐 4 个字节),类似的指令还有
-
sipush 将一个 short 压入操作数栈(其长度会补齐 4 个字节)
-
ldc 将一个 int 压入操作数栈
-
ldc2_w 将一个 long 压入操作数栈(分两次压入,因为 long 是 8 个字节)
-
这里小的数字都是和字节码指令存在一起,超过 short 范围的数字存入了常量池
![image-20221017161445954](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017161445954.png)
istore_1
istore_1
==> 将操作数栈顶数据弹出,存入局部变量表的 slot 1
![image-20221017161534371](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017161534371.png)
ldc #3
ldc #3
==> 从常量池加载 #3 数据到操作数栈
- 注意 Short.MAX_VALUE 是 32767,所以 32768 = Short.MAX_VALUE + 1 实际是在编译期间计算好的
![image-20221017161643684](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017161643684.png)
istore_2
istore_2
==> 从操作数栈顶加载数据到局部变量表插槽2中
![image-20221017162111689](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017162111689.png)
iload_1
iload_1
==> 加载局部变量表插槽1的数据到操作数栈
![image-20221017162742494](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017162742494.png)
iload_2
iload_2
==> 加载局部变量表插槽2的数据到操作数栈
![image-20221017163447557](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017163447557.png)
iadd
iadd ==> 取出栈顶的两个元素, 然后进行累加, 把结果写入操作数栈
![image-20221017163705588](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017163705588.png)
istore_3
istore_3 ==> 将栈顶元素添加到局部变量表的插槽3中
![image-20221017163912307](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017163912307.png)
getstatic #4
- 从运行时常量池中加载 System.out 对象 PrintStream 到 堆中, 然后把对象的引用放入操作数栈汇总
![image-20221017180953459](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017180953459.png)
iload_3
iload_3 ==> 将插槽3中的数据加载到
![image-20221017181241309](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017181241309.png)
invokevirtual #5
- 找到常量池 #5 项
- 定位到方法区 java/io/PrintStream.println:(I)V 方法
- 生成新的栈帧(分配 locals、stack等)
- 传递参数,执行新栈帧中的字节码
![image-20221017185027994](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017185027994.png)
-
执行完毕,弹出栈帧
-
清除 main 操作数栈内容
![image-20221017185226693](https://haloos.oss-cn-beijing.aliyuncs.com/typero/image-20221017185226693.png)
return
- 完成 main 方法调用,弹出 main 栈帧
- 程序结束