字节码介绍
Java 中的字节码,英文名为 bytecode, 是 Java 代码编译后的中间代码格式,JVM 需要读取并解析字节码才能执行相应的任务,是 JVM 的指令集。JVM 加载字节码格式的 class 文件,校验之后通过 JIT 编译器转换为本地机器代码执行。
字节码指令分类
字节码由单字节(byte)的指令组成,理论上最多支持 256(1字节8位,2的8次方) 个操作码(opcode)。实际上 Java 只使用了 200 左右的操作码, 还有一些操作码则保留给调试操作。
操作码, 下面称为 指令, 主要由类型前缀和操作名称两部分组成。
例如,'i' 前缀代表 ‘integer’,所以,'iadd' 很容易理解, 表示对整数执行加法运算。
根据指令的性质,主要分为四个大类:
1.栈操作指令,包括与局部变量交互的指令
2.程序流程控制指令
3.对象操作指令,包括方法调用指令
4.算术运算以及类型转换指令
此外还有一些执行专门任务的指令,比如同步(synchronization)指令,以及抛出异常相关的指令等等。详见JVM字节码指令手册
反编译字节码
1.javac 命令可以将.java转为.class文件:javac JvmLoadDemo.java。
2.java命令执行.class文件:java JvmLoadDemo
3.javap -c命令反编译.class文件,为更加通俗易懂的字节码指令:javap -c JvmLoadDemo
4.javap -c -verbose命令添加了-verbose参数可以在字节码指令的基础上,展示class 文件中的常量池信息。
反编译文件解读示例
public class JvmLoadDemo {
public int i = 3;
public static void main(String[] args) {
JvmLoadDemo jvmLoadDemo = new JvmLoadDemo();
int a = 6;
long b = 7;
long c = a + b;
System.out.println(c);
}
}
将JvmLoadDemo .class反编译,执行 javap -c JvmLoadDemo命令
Compiled from "JvmLoadDemo.java"
public class JvmLoadDemo {
//类变量i
public int i;
//默认构造函数,JVM自己生成
public JvmLoadDemo();
Code:
//从局部变量0中装载引用类型值到操作数栈,
//局部变量表0槽位为何有值?静态方法则没有this 引用,对于非静态方法,this将被默认分配到局部变量表的第 0 号槽位中
0: aload_0
//调用实例方法,常量池中#1的方法
1: invokespecial #1 // Method java/lang/Object."<init>":()V
//将局部变量0值装载到操作数栈,就是this对象装载到操作数栈
4: aload_0
//将int类型常量3压入栈,类变量i=3;
5: iconst_3
//将压入栈的3,赋值给#2,其实就是i变量
6: putfield #2 // Field i:I
//返回void
9: return
//main方法
public static void main(java.lang.String[]);
Code:
//创建一个对象#3,其实是对象JvmLoadDemo`在这里插入代码片`
0: new #3 // class JvmLoadDemo
//复制JvmLoadDemo对象
3: dup
//调用实例方法#4,其实就是初始化对象方法init
4: invokespecial #4 // Method "<init>":()V
//将复制的对象JvmLoadDemo存入局部变量表1槽位,以上几步就是执行的JvmLoadDemo jvmLoadDemo = new JvmLoadDemo()代码
7: astore_1
//将8位的整数压入栈,值是6(对于jvm来说<=8位的都按8位处理,大于8位例如long,double按照16位处理)
8: bipush 6
//将6存入局部变量表2槽位
10: istore_2
//把常量池中long类型的项#5压入栈,值是7L
11: ldc2_w #5 // long 7l
//将栈中的7L存入局部变量表3槽位
14: lstore_3
//从局部变量表的2槽位取值压入栈,值为6
15: iload_2
//将int类型的6转化为long类型
16: i2l
//从局部变量表3的位置取值压入栈,值为7L
17: lload_3
//将栈中long类型做加法运算
18: ladd
//将long的和存入局部变量表5槽位,值为13
19: lstore 5
//获取静态字段 #7
21: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
//从局部变量表5槽位取值,值为13
24: lload 5
//调用对象的实例方法 #8,其实是java/io/PrintStream的println()方法
26: invokevirtual #8 // Method java/io/PrintStream.println:(J)V
//返回void
29: return
}
上面的反编译文件中有大量的#1,#2,#3的引用,要想查看具体#引用的内容需要查看 class 文件中的常量池信息
Classfile /C:/Users/闵祥利/Desktop/我的学习资料/JvmLoadDemo.class
Last modified 2020-9-2; size 479 bytes
MD5 checksum e1eb042afe79246e193650e013559b0a
Compiled from "JvmLoadDemo.java"
public class JvmLoadDemo
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #9.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#21 // JvmLoadDemo.i:I
#3 = Class #22 // JvmLoadDemo
#4 = Methodref #3.#20 // JvmLoadDemo."<init>":()V
#5 = Long 7l
#7 = Fieldref #23.#24 // java/lang/System.out:Ljava/io/PrintStream;
#8 = Methodref #25.#26 // java/io/PrintStream.println:(J)V
#9 = Class #27 // java/lang/Object
#10 = Utf8 i
#11 = Utf8 I
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 main
#17 = Utf8 ([Ljava/lang/String;)V
#18 = Utf8 SourceFile
#19 = Utf8 JvmLoadDemo.java
#20 = NameAndType #12:#13 // "<init>":()V
#21 = NameAndType #10:#11 // i:I
#22 = Utf8 JvmLoadDemo
#23 = Class #28 // java/lang/System
#24 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#25 = Class #31 // java/io/PrintStream
#26 = NameAndType #32:#33 // println:(J)V
#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 (J)V
{
public int i;
descriptor: I
flags: ACC_PUBLIC
public JvmLoadDemo();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_3
6: putfield #2 // Field i:I
9: return
LineNumberTable:
line 2: 0
line 3: 4
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=7, args_size=1
0: new #3 // class JvmLoadDemo
3: dup
4: invokespecial #4 // Method "<init>":()V
7: astore_1
8: bipush 6
10: istore_2
11: ldc2_w #5 // long 7l
14: lstore_3
15: iload_2
16: i2l
17: lload_3
18: ladd
19: lstore 5
21: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
24: lload 5
26: invokevirtual #8 // Method java/io/PrintStream.println:(J)V
29: return
LineNumberTable:
line 5: 0
line 6: 8
line 7: 11
line 8: 15
line 9: 21
line 10: 29
}
SourceFile: "JvmLoadDemo.java"