java 字节码简介
文件
源码:
1
2
3
4
5
6
7package 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
27cafe 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
58public 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)
0000
和 0032
两个字节分别表示class文件的副版本号和主版本号。
常量池数量
001d
,该值等于常量池中的成员数加1
常量池
常量池是一个表结构,它包含class文件结构及其子结构中引用的所有字符串常量、类或接口名、字段名和其它常量。
常量结构:
1
2
3
4cp_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 指令
指令编号:0x2a
。
aload_<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 |