java字节码解析入门,从Hello world开始。
ClassFile 结构
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
java源文件
1 public class Test {
2 public static void main(String[] args) {
3 System.out.println("Hello world");
4 }
5 }
class二进制文件
ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
00 1b 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63
61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01
00 04 74 68 69 73 01 00 06 4c 54 65 73 74 3b 01
00 04 6d 61 69 6e 01 00 16 28 5b 4c 6a 61 76 61
2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 01
00 04 61 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f
6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 53
6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
00 1e 01 00 0b 48 65 6c 6c 6f 20 77 6f 72 6c 64
07 00 1f 0c 00 20 00 21 01 00 04 54 65 73 74 01
00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65
63 74 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53
79 73 74 65 6d 01 00 03 6f 75 74 01 00 15 4c 6a
61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65
61 6d 3b 01 00 13 6a 61 76 61 2f 69 6f 2f 50 72
69 6e 74 53 74 72 65 61 6d 01 00 07 70 72 69 6e
74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00
09 00 00 00 2f 00 01 00 01 00 00 00 05 2a b7 00
01 b1 00 00 00 02 00 0a 00 00 00 06 00 01 00 00
00 01 00 0b 00 00 00 0c 00 01 00 00 00 05 00 0c
00 0d 00 00 00 09 00 0e 00 0f 00 01 00 09 00 00
00 37 00 02 00 01 00 00 00 09 b2 00 02 12 03 b6
00 04 b1 00 00 00 02 00 0a 00 00 00 0a 00 02 00
00 00 03 00 08 00 04 00 0b 00 00 00 0c 00 01 00
00 00 09 00 10 00 11 00 00 00 01 00 12 00 00 00
02 00 13
class二进制文件解析
u4 magic;
占4字节(u4代表4字节、u2代表2字节),类文件标识:cafebabe 咖啡宝贝 👶
- ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- …
u2 minor_version;
占2字节,次版本:0000 minor_version = 0
- ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- …
u2 major_version;
占2字节,主版本:0034 major_version = 52
- ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- …
u2 constant_pool_count;
占2字节,constant_pool_count 项的值等于 constant_pool 表中的条目数加一。 如果 constant_pool 索引大于零且小于 constant_pool_count,则认为它是有效的。
十六进制值:0022 constant_pool_count = 34。表示常量池共有33项。
- ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- …
cp_info constant_pool[constant_pool_count-1];
常量池表,共有33项。constant_pool表索引从1到constant_pool_count - 1。
常量池每项结构如下:
cp_infocp_info { u1 tag; u1 info[]; }
通过一个字节(u1 tag)标识每项类型。
常量类型 tag值 CONSTANT_Class 7 CONSTANT_Fieldref 9 CONSTANT_Methodref 10 CONSTANT_InterfaceMethodref 11 CONSTANT_String 8 CONSTANT_Integer 3 CONSTANT_Float 4 CONSTANT_Long 5 CONSTANT_Double 6 CONSTANT_NameAndType 12 CONSTANT_Utf8 1 CONSTANT_MethodHandle 15 CONSTANT_MethodType 16 CONSTANT_InvokeDynamic 18 以下分别给出CONSTANT_Class、CONSTANT_Methodref、CONSTANT_Fieldref、CONSTANT_String、CONSTANT_Utf8、CONSTANT_NameAndType的结构。
CONSTANT_Class_info { u1 tag; u2 name_index; }
CONSTANT_Methodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
CONSTANT_Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
CONSTANT_String_info { u1 tag; u2 string_index; }
CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; }
CONSTANT_NameAndType_info { u1 tag; u2 name_index; u2 descriptor_index; }
#1 = Methodref #6.#20 //这是第一项,引用第6项和第20项常量,值为:java/lang/Object."<init>": ()V
tag为0a=10,为CONSTANT_Methodref,取5个字节:0a 00 06 00 14
- ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- …
CONSTANT_Methodref_info { u1 tag; 0a = 10 => CONSTANT_Methodref u2 class_index; 00 06 = 6 => 引用第6项常量 u2 name_and_type_index; 00 14 = 20 => 引用第20项常量 }
#2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
tag为09=9,为CONSTANT_Fieldref,取5个字节:09 00 15 00 16
- ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- …
CONSTANT_Fieldref_info { u1 tag; 09 = 9 => CONSTANT_Fieldref u2 class_index; 00 15 = 21 => 引用第21项常量 u2 name_and_type_index; 00 16 = 22 => 引用第22项常量 }
#3 = String #23 // Hello world
- ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- …
CONSTANT_String_info { u1 tag; 08 = 8 => CONSTANT_String u2 string_index; 00 17 = 23 => 引用第23项常量 }
#4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V
- ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- …
CONSTANT_Methodref_info { u1 tag; 0a = 10 => CONSTANT_Methodref u2 class_index; 00 18 = 24 => 引用第24项常量 u2 name_and_type_index; 00 19 = 25 => 引用第25项常量 }
#5 = Class #26 // Test
- ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- …
CONSTANT_Class_info { u1 tag; 07 = 7 => CONSTANT_Class u2 name_index; 00 1a = 26 => 引用第26项常量 }
#6 = Class #27 // java/lang/Object
- 1 ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 2 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- 3 00 1b 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
CONSTANT_Class_info { u1 tag; 07 = 7 => CONSTANT_Class u2 name_index; 00 1b = 27 => 引用第27项常量 }
#7 = Utf8 <init>
- 1 ca fe ba be 00 00 00 34 00 22 0a 00 06 00 14 09
- 2 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- 3 00 1b 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 06 = 6 => 后续跟6个字节长度的字符 u1 bytes[length]; 3c 69 6e 69 74 3e => <init> }
#8 = Utf8 ()V
- 2 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- 3 00 1b 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
- 4 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 03 = 3 => 后续跟3个字节长度的字符 u1 bytes[length]; 28 29 56 => ()V }
#9 = Utf8 Code
- 2 00 15 00 16 08 00 17 0a 00 18 00 19 07 00 1a 07
- 3 00 1b 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
- 4 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 04 = 4 => 后续跟4个字节长度的字符 u1 bytes[length]; 43 6f 64 65 => Code }
#10 = Utf8 LineNumberTable
- 3 00 1b 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
- 4 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
- 5 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 0f = 15 => 后续跟15个字节长度的字符 u1 bytes[length]; 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65=> LineNumberTable }
unicode字符:
4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65#11 = Utf8 LocalVariableTable
- 3 00 1b 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29
- 4 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
- 5 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63
- 6 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 12 = 18 => 后续跟18个字节长度的字符 u1 bytes[length]; 4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 => LocalVariableTable }
unicode字符:
4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65#12 = Utf8 this
- 4 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
- 5 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63
- 6 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01
- 7 00 04 74 68 69 73 01 00 06 4c 54 65 73 74 3b 01
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 04 = 4 => 后续跟4个字节长度的字符 u1 bytes[length]; 74 68 69 73 => this }
#13 = Utf8 LTest;
- 4 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e
- 5 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63
- 6 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01
- 7 00 04 74 68 69 73 01 00 06 4c 54 65 73 74 3b 01
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 06 = 6 => 后续跟6个字节长度的字符 u1 bytes[length]; 4c 54 65 73 74 3b => LTest; }
#14 = Utf8 main
- 5 75 6d 62 65 72 54 61 62 6c 65 01 00 12 4c 6f 63
- 6 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01
- 7 00 04 74 68 69 73 01 00 06 4c 54 65 73 74 3b 01
- 8 00 04 6d 61 69 6e 01 00 16 28 5b 4c 6a 61 76 61
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 04 = 4 => 后续跟4个字节长度的字符 u1 bytes[length]; 6d 61 69 6e => main }
#15 = Utf8 ([Ljava/lang/String;)V
- 6 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65 01
- 7 00 04 74 68 69 73 01 00 06 4c 54 65 73 74 3b 01
- 8 00 04 6d 61 69 6e 01 00 16 28 5b 4c 6a 61 76 61
- 9 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 01
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 16 = 22 => 后续跟22个字节长度的字符 u1 bytes[length]; 28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 => ([Ljava/lang/String;)V }
unicode字符:
28 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56#16 = Utf8 args
- 07 00 04 74 68 69 73 01 00 06 4c 54 65 73 74 3b 01
- 08 00 04 6d 61 69 6e 01 00 16 28 5b 4c 6a 61 76 61
- 09 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 01
- 10 00 04 61 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 04 = 4 => 后续跟4个字节长度的字符 u1 bytes[length]; 61 72 67 73 => args }
#17 = Utf8 [Ljava/lang/String;
- 08 00 04 6d 61 69 6e 01 00 16 28 5b 4c 6a 61 76 61
- 09 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 01
- 10 00 04 61 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f
- 11 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 53
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 13 = 19 => 后续跟19个字节长度的字符 u1 bytes[length]; 5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b => [Ljava/lang/String; }
unicode字符:
5b 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b#18 = Utf8 SourceFile
- 09 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 01
- 10 00 04 61 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f
- 11 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 53
- 12 6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 0a = 10 => 后续跟10个字节长度的字符 u1 bytes[length]; 53 6f 75 72 63 65 46 69 6c 65 => SourceFile }
unicode字符:
53 6f 75 72 63 65 46 69 6c 65#19 = Utf8 Test.java
- 10 00 04 61 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f
- 11 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 53
- 12 6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
- 13 2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 09 = 9 => 后续跟9个字节长度的字符 u1 bytes[length]; 54 65 73 74 2e 6a 61 76 61 => Test.java }
unicode字符:
54 65 73 74 2e 6a 61 76 61#20 = NameAndType #7:#8 // “<init>”: ()V
- 10 00 04 61 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f
- 11 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 53
- 12 6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
- 13 2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
CONSTANT_NameAndType_info { u1 tag; 0c = 12 => CONSTANT_NameAndType u2 name_index; 00 07 = 7 => 引用第7项常量 u2 descriptor_index; 00 08 = 8 => 引用第8项常量 }
#21 = Class #28 // java/lang/System
- 10 00 04 61 72 67 73 01 00 13 5b 4c 6a 61 76 61 2f
- 11 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 53
- 12 6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
- 13 2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
CONSTANT_Class_info { u1 tag; 07 = 7 => CONSTANT_Class u2 name_index; 00 1c = 28 => 引用第28项常量 }
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
- 11 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 53
- 12 6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
- 13 2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
- 14 00 1e 01 00 0b 48 65 6c 6c 6f 20 77 6f 72 6c 64
CONSTANT_NameAndType_info { u1 tag; 0c = 12 => CONSTANT_NameAndType u2 name_index; 00 1d = 29 => 引用第29项常量 u2 descriptor_index; 00 1e = 30 => 引用第30项常量 }
#23 = Utf8 Hello world
- 11 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 01 00 0a 53
- 12 6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
- 13 2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
- 14 00 1e 01 00 0b 48 65 6c 6c 6f 20 77 6f 72 6c 64
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 0b = 11 => 后续跟11个字节长度的字符 u1 bytes[length]; 48 65 6c 6c 6f 20 77 6f 72 6c 64 => Hello world }
unicode字符:
48 65 6c 6c 6f 20 77 6f 72 6c 64#24 = Class #31 // java/io/PrintStream
- 12 6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
- 13 2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
- 14 00 1e 01 00 0b 48 65 6c 6c 6f 20 77 6f 72 6c 64
- 15 07 00 1f 0c 00 20 00 21 01 00 04 54 65 73 74 01
CONSTANT_Class_info { u1 tag; 07 = 7 => CONSTANT_Class u2 name_index; 00 1f = 31 => 引用第31项常量 }
#25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V
- 12 6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
- 13 2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
- 14 00 1e 01 00 0b 48 65 6c 6c 6f 20 77 6f 72 6c 64
- 15 07 00 1f 0c 00 20 00 21 01 00 04 54 65 73 74 01
CONSTANT_NameAndType_info { u1 tag; 0c = 12 => CONSTANT_NameAndType u2 name_index; 00 20 = 32 => 引用第32项常量 u2 descriptor_index; 00 21 = 33 => 引用第33项常量 }
#26 = Utf8 Test
- 12 6f 75 72 63 65 46 69 6c 65 01 00 09 54 65 73 74
- 13 2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
- 14 00 1e 01 00 0b 48 65 6c 6c 6f 20 77 6f 72 6c 64
- 15 07 00 1f 0c 00 20 00 21 01 00 04 54 65 73 74 01
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 04 = 4 => 后续跟4个字节长度的字符 u1 bytes[length]; 54 65 73 74 => Test }
#27 = Utf8 java/lang/Object
- 13 2e 6a 61 76 61 0c 00 07 00 08 07 00 1c 0c 00 1d
- 14 00 1e 01 00 0b 48 65 6c 6c 6f 20 77 6f 72 6c 64
- 15 07 00 1f 0c 00 20 00 21 01 00 04 54 65 73 74 01
- 16 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65
- 17 63 74 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 10 = 16 => 后续跟16个字节长度的字符 u1 bytes[length]; 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 => java/lang/Object }
unicode字符:
6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74#28 = Utf8 java/lang/System
- 14 00 1e 01 00 0b 48 65 6c 6c 6f 20 77 6f 72 6c 64
- 15 07 00 1f 0c 00 20 00 21 01 00 04 54 65 73 74 01
- 16 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65
- 17 63 74 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53
- 18 79 73 74 65 6d 01 00 03 6f 75 74 01 00 15 4c 6a
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 10 = 16 => 后续跟16个字节长度的字符 u1 bytes[length]; 6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 65 6d => java/lang/System }
unicode字符:
6a 61 76 61 2f 6c 61 6e 67 2f 53 79 73 74 65 6d#29 = Utf8 out
- 15 07 00 1f 0c 00 20 00 21 01 00 04 54 65 73 74 01
- 16 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65
- 17 63 74 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53
- 18 79 73 74 65 6d 01 00 03 6f 75 74 01 00 15 4c 6a
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 03 = 3 => 后续跟3个字节长度的字符 u1 bytes[length]; 6f 75 74 => out }
#30 = Utf8 Ljava/io/PrintStream;
- 16 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65
- 17 63 74 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53
- 18 79 73 74 65 6d 01 00 03 6f 75 74 01 00 15 4c 6a
- 19 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65
- 20 61 6d 3b 01 00 13 6a 61 76 61 2f 69 6f 2f 50 72
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 15 = 21 => 后续跟21个字节长度的字符 u1 bytes[length]; 4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 3b => Ljava/io/PrintStream; }
unicode字符:
4c 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d 3b#31 = Utf8 java/io/PrintStream
- 17 63 74 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 53
- 18 79 73 74 65 6d 01 00 03 6f 75 74 01 00 15 4c 6a
- 19 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65
- 20 61 6d 3b 01 00 13 6a 61 76 61 2f 69 6f 2f 50 72
- 21 69 6e 74 53 74 72 65 61 6d 01 00 07 70 72 69 6e
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 13 = 19 => 后续跟19个字节长度的字符 u1 bytes[length]; 6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d => java/io/PrintStream }
unicode字符:
6a 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65 61 6d#32 = Utf8 println
- 18 79 73 74 65 6d 01 00 03 6f 75 74 01 00 15 4c 6a
- 19 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65
- 20 61 6d 3b 01 00 13 6a 61 76 61 2f 69 6f 2f 50 72
- 21 69 6e 74 53 74 72 65 61 6d 01 00 07 70 72 69 6e
- 22 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 07 = 7 => 后续跟7个字节长度的字符 u1 bytes[length]; 70 72 69 6e 74 6c 6e => println }
unicode字符:
70 72 69 6e 74 6c 6e#33 = Utf8 (Ljava/lang/String;)V
- 19 61 76 61 2f 69 6f 2f 50 72 69 6e 74 53 74 72 65
- 20 61 6d 3b 01 00 13 6a 61 76 61 2f 69 6f 2f 50 72
- 21 69 6e 74 53 74 72 65 61 6d 01 00 07 70 72 69 6e
- 22 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
- 23 67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
CONSTANT_Utf8_info { u1 tag; 01 = 1 => CONSTANT_Utf8 u2 length; 00 15 = 21 => 后续跟21个字节长度的字符 u1 bytes[length]; 28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56 => (Ljava/lang/String;)V }
unicode字符:
28 4c 6a 61 76 61 2f 6c 61 6e 67 2f 53 74 72 69 6e 67 3b 29 56常量表到此结束。
u2 access_flags;
flags: ACC_PUBLIC, ACC_SUPER
access_flags 项的值是一个标志掩码,用于表示对此类或接口的访问权限和属性。
Flag Name tag值 解释 ACC_PUBLIC 0x0001 公开; 可以从其包外部访问。 ACC_FINAL 0x0010 声明为最终; 不允许子类。 ACC_SUPER 0x0020 当被 invokespecial 指令调用时,特别对待超类方法。 ACC_INTERFACE 0x0200 是接口,不是类。 ACC_ABSTRACT 0x0400 声明为抽象类; 不得实例化。 ACC_SYNTHETIC 0x1000 声明合成; 源代码中不存在。 ACC_ANNOTATION 0x2000 声明为注释类型。 ACC_ENUM 0x4000 声明为枚举类型。 access_flags=0021=0x0001+0x0020=ACC_PUBLIC, ACC_SUPER
- 20 61 6d 3b 01 00 13 6a 61 76 61 2f 69 6f 2f 50 72
- 21 69 6e 74 53 74 72 65 61 6d 01 00 07 70 72 69 6e
- 22 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
- 23 67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
u2 this_class;
class: Test
this_class=0005=5 =>指向常量表第5项: #5 = Class #26 // Test
- 21 69 6e 74 53 74 72 65 61 6d 01 00 07 70 72 69 6e
- 22 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
- 23 67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
u2 super_class;
super class: java/lang/Object
super_class=0006=6 =>指向常量表第6项: #6 = Class #27 // java/lang/Object
- 22 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
- 23 67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
- 24 06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00
u2 interfaces_count;
interfaces_count: 0
interfaces_count=0000=0
- 22 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
- 23 67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
- 24 06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00
u2 interfaces[interfaces_count];
interfaces_count为0,该项为空,不占用字节。
u2 fields_count;
fields_count: 0
fields_count=0000=0
- 22 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
- 23 67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
- 24 06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00
field_info fields[fields_count];
fields_count为0,该项为空,不占用字节。
u2 methods_count;
methods_count: 2,有两个方法。
methods_count=0002=2
- 22 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
- 23 67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
- 24 06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00
method_info methods[methods_count];
method结构如下:
method_infomethod_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }
access_flags:Method访问和属性标志
Flag Name tag值 解释 ACC_PUBLIC 0x0001 公开; 可以从其包外部访问。 ACC_PRIVATE 0x0002 声明为私有; 只能在定义类中访问。 ACC_PROTECTED 0x0004 声明受保护; 可以在子类中访问。 ACC_STATIC 0x0008 声明为静态。 ACC_FINAL 0x0010 声明为最终; 不得被覆盖。 ACC_SYNCHRONIZED 0x0020 声明同步; 调用由监视器使用包装。 ACC_BRIDGE 0x0040 由编译器生成的桥接方法。 ACC_VARARGS 0x0080 用可变数量的参数声明。 ACC_NATIVE 0x0100 声明为本地; 用 Java 以外的语言实现。 ACC_ABSTRACT 0x0400 声明为抽象类; 没有提供实现。 ACC_STRICT 0x0800 声明的strictfp; 浮点模式是 FP-strict。 ACC_SYNTHETIC 0x1000 声明合成; 源代码中不存在。 #method1: public Test();
- flags: ACC_PUBLIC
- name: <init>
- descriptor: ()V
- attributes_count: 1
- 22 74 6c 6e 01 00 15 28 4c 6a 61 76 61 2f 6c 61 6e
- 23 67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
- 24 06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00
method_info { u2 access_flags; 00 01 => ACC_PUBLIC u2 name_index; 00 07 = 7 => #7 = Utf8 <init> u2 descriptor_index; 00 08 = 8 => #8 = Utf8 ()V u2 attributes_count; 00 01 = 1 attribute_info attributes[attributes_count]; }
attributes说明
attributes: 共23种类型的attribute
attributes通用格式如下:
attribute_infoattribute_info { u2 attribute_name_index; //常量表序号,attribute的名称 u4 attribute_length;//attribute占用的字节长度,不包括attribute_name_index和attribute_length占用的6个字节 u1 info[attribute_length];//字节数组 }
#method1
attribute name: Code
attribute 长度: 47字节
- 23 67 2f 53 74 72 69 6e 67 3b 29 56 00 21 00 05 00
- 24 06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00
- 25 09 00 00 00 2f 00 01 00 01 00 00 00 05 2a b7 00
attribute_info { u2 attribute_name_index; 00 09 = 9 => #9 = Utf8 Code u4 attribute_length; 00 00 00 2f = 47 u1 info[attribute_length]; }
Code attribute说明
Code attribute格式如下:
Code_attributeCode_attribute { u2 attribute_name_index; u4 attribute_length; u2 max_stack; u2 max_locals; u4 code_length; u1 code[code_length]; u2 exception_table_length; { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; attribute_info attributes[attributes_count]; }
指令集说明
#method1 前两个字节指示的是code,因此attribute_info需要转成具体的Code_attribute
- Code attribute name: Code
- Code attribute 长度: 47字节
- stack=1, locals=1
- 0: aload_0
- 1: invokespecial #1
- 4: return
- LineNumberTable: line 1: 0
- LocalVariableTable: Start:0 Length:5 Slot:0 Name:this Signature:LTest;
- 24 06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00
- 25 09 00 00 00 2f 00 01 00 01 00 00 00 05 2a b7 00
- 26 01 b1 00 00 00 02 00 0a 00 00 00 06 00 01 00 00
- 27 00 01 00 0b 00 00 00 0c 00 01 00 00 00 05 00 0c
- 28 00 0d 00 00 00 09 00 0e 00 0f 00 01 00 09 00 00
Code_attribute { u2 attribute_name_index; 00 09 = 9 => #9 = Utf8 Code u4 attribute_length; 00 00 00 2f = 47 u2 max_stack; 00 01 = 1 => stack=1 u2 max_locals; 00 01 = 1 => locals=1 u4 code_length; 00 00 00 05 = 5 => 后续5个字节为命令区 u1 code[code_length]; 2a : b7 00 01 : b1 按照指令集格式解析成3个指令 aload_0、 invokespecial #1 和 return 2a = 42 => aload_0 = 42 (0x2a) this指针入操作数栈 b7 00 01 => (invokespecial indexbyte1 indexbyte2) 调用默认构造函数 b7 = 183 => invokespecial = 183 (0xb7) 00 01 => #1 = Methodref #6.#20 // java/lang/Object."<init>":()V b1 = 177 => return = 177 (0xb1) 返回,退出操作数栈 u2 exception_table_length; 00 00 = 0 无异常表 { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; 00 02 = 2 两个属性,后续字节包含两个attribute attribute_info attributes[attributes_count]; attributes[2]解析得LineNumberTable和LocalVariableTable两个属性 LineNumberTable_attribute { u2 attribute_name_index; 00 0a = 10 => #10 = Utf8 LineNumberTable u4 attribute_length; 00 00 00 06 = 6 u2 line_number_table_length; 00 01 = 1 { u2 start_pc; 00 00 = 0 u2 line_number; 00 01 = 1 } line_number_table[line_number_table_length]; } LocalVariableTable_attribute { u2 attribute_name_index;00 0b = 11 => #11 = Utf8 LocalVariableTable u4 attribute_length; 00 00 00 0c = 12 u2 local_variable_table_length; 00 01 = 1 { u2 start_pc; 00 00 = 0 u2 length; 00 05 = 5 //参考前面5个字节的命令区 u2 name_index; 00 0c = 12 => #12 = Utf8 this u2 descriptor_index; 00 0d = 13 => #13 = Utf8 LTest; u2 index; 00 00 = 0 } local_variable_table[local_variable_table_length]; } }
对应如下代码:
Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0 // 指令aload_0对应于第一行源代码 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTest;
#method2: public static void main(java.lang.String[]);
- flags: ACC_PUBLIC, ACC_STATIC
- name: main
- descriptor: ([Ljava/lang/String;)V
- attributes_count: 1
- 24 06 00 00 00 00 00 02 00 01 00 07 00 08 00 01 00
- 25 09 00 00 00 2f 00 01 00 01 00 00 00 05 2a b7 00
- 26 01 b1 00 00 00 02 00 0a 00 00 00 06 00 01 00 00
- 27 00 01 00 0b 00 00 00 0c 00 01 00 00 00 05 00 0c
- 28 00 0d 00 00 00 09 00 0e 00 0f 00 01 00 09 00 00
method_info { u2 access_flags; 00 09 = 0x0001 + 0x0008 => ACC_PUBLIC + ACC_STATIC u2 name_index; 00 0e = 14 => #14 = Utf8 main u2 descriptor_index; 00 0f = 15 => #15 = Utf8 ([Ljava/lang/String;)V u2 attributes_count; 00 01 = 1 attribute_info attributes[attributes_count]; }
#method2
- Code attribute name: Code
- Code attribute 长度: 55字节
- stack=2, locals=1
- 0: getstatic #2
- 3: ldc #3
- 5: invokevirtual #4
- 4: return
- LineNumberTable: line 3: 0 line 4: 8
- LocalVariableTable: Start:0 Length:9 Slot:0 Name:args Signature:[Ljava/lang/String;
- 27 00 01 00 0b 00 00 00 0c 00 01 00 00 00 05 00 0c
- 28 00 0d 00 00 00 09 00 0e 00 0f 00 01 00 09 00 00
- 29 00 37 00 02 00 01 00 00 00 09 b2 00 02 12 03 b6
- 30 00 04 b1 00 00 00 02 00 0a 00 00 00 0a 00 02 00
- 31 00 00 03 00 08 00 04 00 0b 00 00 00 0c 00 01 00
- 32 00 00 09 00 10 00 11 00 00 00 01 00 12 00 00 00
- 33 02 00 13
Code_attribute { u2 attribute_name_index; 00 09 = 9 => #9 = Utf8 Code u4 attribute_length; 00 00 00 37 = 55 u2 max_stack; 00 02 = 2 => stack=2 u2 max_locals; 00 01 = 1 => locals=1 u4 code_length; 00 00 00 09 = 9 => 后续9个字节为命令区 u1 code[code_length]; b2 00 02: 12 03: b6 00 04: b1 按照指令集格式解析成4个指令getstatic #2、ldc #3、invokevirtual #4 和 return b2 00 02 => (getstatic indexbyte1 indexbyt2) getstatic = 178 (0xb2) #2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream; 12 03 => (ldc index) ldc = 18 (0x12) #3 = String #23 // Hello world b6 00 04 => (invokevirtual indexbyte1 indexbyt2) invokevirtual = 182 (0xb6) #4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V b1 = 177 => return = 177 (0xb1) 返回,退出操作数栈 u2 exception_table_length; 00 00 = 0 无异常表 { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_length]; u2 attributes_count; 00 02 = 2 两个属性,后续字节包含两个attribute attribute_info attributes[attributes_count]; attributes[2]解析得LineNumberTable和LocalVariableTable两个属性 LineNumberTable_attribute { u2 attribute_name_index; 00 0a = 10 => #10 = Utf8 LineNumberTable u4 attribute_length; 00 00 00 0a = 10 u2 line_number_table_length; 00 02 = 2 { u2 start_pc; 00 00 = 0 u2 line_number; 00 03 = 3 } line_number_table[line_number_table_length]; // line 3: 0 { u2 start_pc; 00 08 = 8 u2 line_number; 00 04 = 4 } line_number_table[line_number_table_length]; // line 4: 8 } LocalVariableTable_attribute { u2 attribute_name_index;00 0b = 11 => #11 = Utf8 LocalVariableTable u4 attribute_length; 00 00 00 0c = 12 u2 local_variable_table_length; 00 01 = 1 { u2 start_pc; 00 00 = 0 u2 length; 00 09 = 9 //参考前面9个字节的命令区 u2 name_index; 00 10 = 16 => #16 = Utf8 args u2 descriptor_index; 00 11 = 17 => #17 = Utf8 [Ljava/lang/String; u2 index; 00 00 = 0 } local_variable_table[local_variable_table_length]; } }
对应如下代码:
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 3: 0 // 指令getstatic对应于第3行源代码 line 4: 8 // 指令return对应于第4行源代码 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String;
u2 attributes_count;
占2字节,0001 attributes_count = 1
- 31 00 00 03 00 08 00 04 00 0b 00 00 00 0c 00 01 00
- 32 00 00 09 00 10 00 11 00 00 00 01 00 12 00 00 00
- 33 02 00 13
attribute_info attributes[attributes_count];
SourceFile: “Test.java”
attribute_name=SourceFile,启用 SourceFile_attribute 格式解析
- 31 00 00 03 00 08 00 04 00 0b 00 00 00 0c 00 01 00
- 32 00 00 09 00 10 00 11 00 00 00 01 00 12 00 00 00
- 33 02 00 13
attribute_info attributes[attributes_count]; SourceFile_attribute { u2 attribute_name_index; 00 12 = 18 => #18 = Utf8 SourceFile u4 attribute_length; 00 00 00 02 = 2 u2 sourcefile_index; 00 13 = 19 => #19 = Utf8 Test.java }
END
javap -v 结果
public class Test 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 // Test #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 LTest; #14 = Utf8 main #15 = Utf8 ([Ljava/lang/String;)V #16 = Utf8 args #17 = Utf8 [Ljava/lang/String; #18 = Utf8 SourceFile #19 = Utf8 Test.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 Test #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 Test(); 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 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTest; 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 world 5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 line 4: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 args [Ljava/lang/String; } SourceFile: "Test.java"