2. 字节码指令
2.1 入门
public cn.itcast.jvm.t5.HelloWorld();构造方法的字节码指令
2a b7 00 01 b1
- 2a=> aload_0加载slot 0的局部变量,即this,做为下面的invokespecial 构造方法调用的参数
- b7 =>invokespecial预备调用构造方法,哪个方法呢?
- 00 01引用常量池中#1项,即【Method Java/lang/Object.""😦)V】
- b1表示返回(return)
另一个是public static void main(java.lang.String[]);主方法的字节码指令
b2 00 02 12 03 b6 00 04 b1
- b2=>getstatic 用来加载静态变量,哪个静态变量呢?(System.out)
- 00 02引用常量池中#2项,即【Fied java/lang/System.out:Ljava/io/PrintStream;】
- 12=>ldc 加载参数,哪个参数呢?(hello world常量)
- 03引用常量池中#3项,即【String hello world】
- b6=>invokirtual 预备调用成员方法,哪个方法呢?(println(String)V
- 00 04引用常量池中#4项,即【Method java/io/PrintStream.println:(Ljava/lang/String;)V】
- b1表示返回
2.2javap工具
Oracle提供了javap工具来反编译class文件
2.3图解方法执行流程
(1)原始Java代码
package cn.itcast.jvm.t3.bytecode;
/**
*演示 字节码指令 和 操作数栈、常量池的关系
*/
public class Demo3_1{public static void main(String[] args){int a=10;int b=Short.MAX_VALUE+1;int c=a+b;System.out.println(c);}
}
(2)编译后的字节码文件
(3)常量池载入运行时常量池
(4)方法字节码载入方法区
(5)main线程开始运行,分配栈帧内存
(stack=2,locals=4)
(6)执行引擎开始执行字节码
bipush 10
- 将一个byte压入操作数栈(其长度会补齐4个字节),类似的指令还有
- sipush将一个short压入操作数栈(其长度会补齐4个字节)
- ldc将一个int压入操作数栈
- ldc2_w将一个long压入操作数栈(分两次压入,因为long是8个字节)
- 这里小的数字都是和字节码指令存在一起,超过short范围的数字存入了常量池
istore_1
- 将操作数栈顶数据弹出,存入局部变量表的slot 1
ldc #3
- 从常量池加载#3数据到操作数栈
- 注意Short.MAX_VALUE是32767,所有32768=Short.MAX_VALUE+1实际是在编译期间计算好的
istore_2
iload_1
iload_2
iadd
istore_3
getstatic #4
iload_3
invokevirtual #5
- 找到常量池#5项
- 定位找到方法区Java/io/PrintStream.println:(I)V方法
- 生成新的栈帧(分配locals、stack等)
- 传递参数,执行新栈帧中的字节码
- 执行完毕,弹出栈帧
- 清除main操作数栈内容
return
- 完成main方法调用,弹出main栈帧
- 程序结束