java 字节码简介

文件

源码:

1
2
3
4
5
6
7
package com.xiaotian;

public class Simple {
public static void main(String[] args) {
System.out.println("hello");
}
}

字节码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
cafe babe 0000 0034 001d 0a00 0600 0f09
0010 0011 0800 120a 0013 0014 0700 1507
0016 0100 063c 696e 6974 3e01 0003 2829
5601 0004 436f 6465 0100 0f4c 696e 654e
756d 6265 7254 6162 6c65 0100 046d 6169
6e01 0016 285b 4c6a 6176 612f 6c61 6e67
2f53 7472 696e 673b 2956 0100 0a53 6f75
7263 6546 696c 6501 000b 5369 6d70 6c65
2e6a 6176 610c 0007 0008 0700 170c 0018
0019 0100 0568 656c 6c6f 0700 1a0c 001b
001c 0100 1363 6f6d 2f78 6961 6f74 6961
6e2f 5369 6d70 6c65 0100 106a 6176 612f
6c61 6e67 2f4f 626a 6563 7401 0010 6a61
7661 2f6c 616e 672f 5379 7374 656d 0100
036f 7574 0100 154c 6a61 7661 2f69 6f2f
5072 696e 7453 7472 6561 6d3b 0100 136a
6176 612f 696f 2f50 7269 6e74 5374 7265
616d 0100 0770 7269 6e74 6c6e 0100 1528
4c6a 6176 612f 6c61 6e67 2f53 7472 696e
673b 2956 0021 0005 0006 0000 0000 0002
0001 0007 0008 0001 0009 0000 001d 0001
0001 0000 0005 2ab7 0001 b100 0000 0100
0a00 0000 0600 0100 0000 0300 0900 0b00
0c00 0100 0900 0000 2500 0200 0100 0000
09b2 0002 1203 b600 04b1 0000 0001 000a
0000 000a 0002 0000 0005 0008 0006 0001
000d 0000 0002 000e

使用 javap -v 反汇编:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class com.xiaotian.Simple
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#15 // java/lang/Object."<init>":()V
#2 = Fieldref #16.#17 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #18 // hello
#4 = Methodref #19.#20 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #21 // com/xiaotian/Simple
#6 = Class #22 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Simple.java
#15 = NameAndType #7:#8 // "<init>":()V
#16 = Class #23 // java/lang/System
#17 = NameAndType #24:#25 // out:Ljava/io/PrintStream;
#18 = Utf8 hello
#19 = Class #26 // java/io/PrintStream
#20 = NameAndType #27:#28 // println:(Ljava/lang/String;)V
#21 = Utf8 com/xiaotian/Simple
#22 = Utf8 java/lang/Object
#23 = Utf8 java/lang/System
#24 = Utf8 out
#25 = Utf8 Ljava/io/PrintStream;
#26 = Utf8 java/io/PrintStream
#27 = Utf8 println
#28 = Utf8 (Ljava/lang/String;)V
{
public com.xiaotian.Simple();
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 3: 0

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 5: 0
line 6: 8
}

分解

Magic(魔数)

Magic 的唯一作用是确定这个文件是否为一个能被java虚拟机接受的class文件。魔数固定值为: 0xCAFEBABE,不会改变。

主版本号(major_version) 和 副版本号(minor_version)

00000032 两个字节分别表示class文件的副版本号和主版本号。

常量池数量

001d,该值等于常量池中的成员数加1

常量池

常量池是一个表结构,它包含class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量。
常量结构:

1
2
3
4
cp_info {
u1 tag;
u1 info[];
}

根据此结构,可拆分常量:

编号 二进制 类型 解释
1 0a 00 0600 0f CONSTANT_Methodref 方法:#6.#15 => java/lang/Object.<init>()V
2 09 0010 0011 CONSTANT_Fieldref_info 方法:#16.#17 => com/xiaotian/Simple.out.Ljava/io/PrintStream
3 08 00 12 CONSTANT_String 字符串:#18 => hello
4 0a 0013 0014 CONSTANT_Methodref 方法:#19.#20 => java/io/PrintStream.println(Ljava/lang/String;)V
5 07 00 15 CONSTANT_Class 类:#21 => com/xiaotian/Simple
6 07 0016 CONSTANT_Class 类:#22 => java/lang/Object
7 01 00 063c 696e 6974 3e CONSTANT_Utf8 字符串常量:<init>
8 01 0003 2829 56 CONSTANT_Utf8 字符串常量:()V
9 01 0004 436f 6465 CONSTANT_Utf8 字符串常量:Code
10 01 00 0f4c 696e 654e 756d 6265 7254 6162 6c65 CONSTANT_Utf8 字符串常量:LineNumberTable
11 01 00 046d 6169 6e CONSTANT_Utf8 字符串常量:main
12 01 0016 285b 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 2956 CONSTANT_Utf8 字符串常量:([Ljava/lang/String;)V
13 01 00 0a53 6f75 7263 6546 696c 65 CONSTANT_Utf8 字符串常量:SourceFile
14 01 000b 5369 6d70 6c65 2e6a 6176 61 CONSTANT_Utf8 字符串常量:Simple.java
15 0c 0007 0008 CONSTANT_NameAndType 字段或方法:#7.#8 => <init>()V
16 07 00 17 CONSTANT_Class 类:#21 => com/xiaotian/Simple
17 0c 0018 0019 CONSTANT_NameAndType 字段或方法:#24.#25 => out.Ljava/io/PrintStream
18 01 00 0568 656c 6c6f CONSTANT_Utf8 字符串常量:hello
19 07 00 1a CONSTANT_Class 类:#26 => java/io/PrintStream
20 0c 001b 001c CONSTANT_NameAndType 字段或方法:#27.#28 => println(Ljava/lang/String;)V
21 01 00 1363 6f6d 2f78 6961 6f74 6961 6e2f 5369 6d70 6c65 CONSTANT_Utf8 字符串常量:com/xiaotian/Simple
22 01 00 106a 6176 612f 6c61 6e67 2f4f 626a 6563 74 CONSTANT_Utf8 字符串常量:java/lang/Object
23 01 0010 6a61 7661 2f6c 616e 672f 5379 7374 656d CONSTANT_Utf8 字符串常量:java/lang/System
24 01 00 036f 7574 CONSTANT_Utf8 字符串常量:out
25 01 00 154c 6a61 7661 2f69 6f2f 5072 696e 7453 7472 6561 6d3b CONSTANT_Utf8 字符串常量:Ljava/io/PrintStream;
26 01 00 136a 6176 612f 696f 2f50 7269 6e74 5374 7265 616d CONSTANT_Utf8 字符串常量:java/io/PrintStream
27 01 00 0770 7269 6e74 6c6e CONSTANT_Utf8 字符串常量:println
28 01 00 1528 4c6a 6176 612f 6c61 6e67 2f53 7472 696e 673b 2956 CONSTANT_Utf8 字符串常量:(Ljava/lang/String;)V

类属性

0021 # access flags 0005 # this_class 0006 # super_class 0000 # interfaces_count 0000 # fields_count

方法

0002 # methods_count

方法 1

二进制 描述
0001 方法访问权限及属性标志:ACC_PUBLIC
0007 方法名索引: #7 => <init>
0008 方法描述符: #8 => ()V
0001 附加属性数量: 1

Code属性

二进制 描述
0009 属性名称:#9 => Code
0000 001d 属性长度:29
0001 max_stack = 1 。当前方法的操作数栈在方法执行的任何时间点的最大深度。
0001 max_locals = 1 。当前方法的局部变量个数。
0000 0005 code_length = 5
2ab7 0001 b1 aload_0 invokespecial #1 return
00 00 exception_table_length
00 01 attributes_count = 1
00 0a00 0000 0600 0100 0000 03 attribute_info 参考下表
二进制 描述
00 0a 属性名称:#10 => LineNumberTable
00 0000 06 属性数量:6
00 01 line_number_table_length = 1
00 0000 03 start_pc = 0, line_number = 3 => line 3: 0

aload_0 指令

指令编号:0x2aaload_<n> 从局部变量表加载一个 reference 类型值到操作数栈中。<n> 代表当前栈帧中局部变量表的索引值。该变量必须为 reference 类型,成为: objectref

invokespecial 指令

指令编号:0xb7。 调用实例方法,专门用来调用父类方法、私有方法和实例初始化方法。 语法:invokespecial <2字节的运行时常量池索引值>

return 指令

指令编号:0xb1。 方法中返回void。

方法 2

二进制 描述
00 09 方法访问权限及属性标志:ACC_PUBLIC | ACC_STATIC
00 0b 方法名索引: #11 => main
00 0c 方法描述符: #12 => ([Ljava/lang/String;)V
00 01 附加属性数量: 1

Code属性

二进制 描述
00 09 属性名称:#9 => Code
00 0000 25 属性长度:37
00 02 max_stack = 2 。当前方法的操作数栈在方法执行的任何时间点的最大深度。
00 01 max_locals = 1 。当前方法的局部变量个数。
00 0000 09 code_length = 9
b2 0002 getstatic #2
1203 ldc #3
b600 04 invokevirtual #4
b1 return
0000 exception_table_length
0001 attributes_count = 1
000a 0000 000a 0002 0000 0005 0008 0006 attribute_info 参考下表
二进制 描述
000a 属性名称:#10 => LineNumberTable
0000 000a 属性长度:10
0002 line_number_table_length = 2
0000 0005 start_pc = 0, line_number = 5 => line 5: 0
0008 0006 start_pc = 8, line_number = 6 => line 6: 8

字段

二进制 描述
0001 属性数量: 1
000d 属性名:#13 => SourceFile
0000 0002 属性长度。 SourceFile 类型的长度必须是 2
000e sourcefile_index: #14 => Simple.java
java 字节码