2020-11-04 | Java | Unlock

JVM-day03

JVM-day03-01.png

1.类文件结构

一个简单的HelloWorld.java:

1
2
3
4
5
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello world");
}
}

执行 javac 命令:

1
javac -parameters -d . HelloWorld.java

得到HelloWorld.class文件

这里使用的是Windows环境,使用UltraEdit或者WinHex工具打开class文件。如果是Linux环境,可以使用如下命令打开class文件:

1
od -t xC HelloWorld.class

打开class文件结果如下:

JVM-day03-02.png

根据JVM规范,类文件结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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];
}

参考资料:Oracle官方文档

1.1 魔数

1
u4 magic;

The magic item supplies the magic number identifying the class file format; it has the value 0xCAFEBABE.

1
00000000h: CA FE BA BE 00 00 00 34 00 23 0A 00 06 00 15 09 

0-3字节,CA FE BA BE 表示是否是class类型的文件

1.2 版本

1
2
u2 minor_version;
u2 major_version;
1
00000000h: CA FE BA BE 00 00 00 34 00 23 0A 00 06 00 15 09 

4-7字节,00 00 00 34 表示类的版本

0x34(十进制52)表明是java8

1.3 常量池

1
2
u2          constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
Constant Type Value
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
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
00000000h: CA FE BA BE 00 00 00 34 00 23 0A 00 06 00 15 09
00000010h: 00 16 00 17 08 00 18 0A 00 19 00 1A 07 00 1B 07
00000020h: 00 1C 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29
00000030h: 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E
00000040h: 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63
00000050h: 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01
00000060h: 00 04 74 68 69 73 01 00 24 4C 70 75 62 2F 69 79
00000070h: 75 2F 73 65 2F 6A 76 6D 64 61 79 2F 64 61 79 30
00000080h: 33 2F 48 65 6C 6C 6F 57 6F 72 6C 64 3B 01 00 04
00000090h: 6D 61 69 6E 01 00 16 28 5B 4C 6A 61 76 61 2F 6C
000000a0h: 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 04
000000b0h: 61 72 67 73 01 00 13 5B 4C 6A 61 76 61 2F 6C 61
000000c0h: 6E 67 2F 53 74 72 69 6E 67 3B 01 00 10 4D 65 74
000000d0h: 68 6F 64 50 61 72 61 6D 65 74 65 72 73 01 00 0A
000000e0h: 53 6F 75 72 63 65 46 69 6C 65 01 00 0F 48 65 6C
000000f0h: 6C 6F 57 6F 72 6C 64 2E 6A 61 76 61 0C 00 07 00
00000100h: 08 07 00 1D 0C 00 1E 00 1F 01 00 0B 48 65 6C 6C
00000110h: 6F 20 77 6F 72 6C 64 07 00 20 0C 00 21 00 22 01
00000120h: 00 22 70 75 62 2F 69 79 75 2F 73 65 2F 6A 76 6D
00000130h: 64 61 79 2F 64 61 79 30 33 2F 48 65 6C 6C 6F 57
00000140h: 6F 72 6C 64 01 00 10 6A 61 76 61 2F 6C 61 6E 67
00000150h: 2F 4F 62 6A 65 63 74 01 00 10 6A 61 76 61 2F 6C
00000160h: 61 6E 67 2F 53 79 73 74 65 6D 01 00 03 6F 75 74
00000170h: 01 00 15 4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E
00000180h: 74 53 74 72 65 61 6D 3B 01 00 13 6A 61 76 61 2F
00000190h: 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 01 00
000001a0h: 07 70 72 69 6E 74 6C 6E 01 00 15 28 4C 6A 61 76
000001b0h: 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56

8-9字节,表示常量池长度,00 23(十进制35)表示常量池有#1-#34项,注意#0项不计入,也没有值

cp_info: #1-#34

第#1项 0A 00 06 00 15

0A表示一个Method信息CONSTANT_Methodref,00 06(十进制6) 和 00 15(十进制21)表示它引用了常量池中#6和#21项来获得这个方法的[所属类]和[方法名]

第#2项 09 00 16 00 17

09表示一个Field信息CONSTANT_Fieldref,00 16(十进制22) 和 00 17(十进制23)表示它引用了常量池中#22和#23项来获得这个方法的[所属类]和[成员变量名]

第#3项 08 00 18

08表示一个字符串常量名称CONSTANT_String,00 18(十进制24)表示它引用了常量池中#24项

第#4项 0A 00 19 00 1A

0A表示一个Method信息CONSTANT_Methodref,00 19(十进制25) 和 00 1A(十进制26)表示它引用了常量池中#25和#26项来获得这个方法的[所属类]和[方法名]

第#5项 07 00 1B

07表示一个Class信息CONSTANT_Class,00 1B(十进制27)表示它引用了常量池中#27项

第#6项 07 00 1C

07表示一个Class信息CONSTANT_Class,00 1C(十进制28)表示它引用了常量池中#28项

第#7项 01 00 06 3C 69 6E 69 74 3E

01表示一个Utf8串CONSTANT_Utf8,00 06(十进制6) 表示长度,3C 69 6E 69 74 3E 是【】(构造方法)

第#8项 01 00 03 28 29 56

01表示一个Utf8串CONSTANT_Utf8,00 03(十进制3)表示长度,28 29 56是【()V】其实就是表示无参,无返回值

第#9项 01 00 04 43 6F 64 65

01表示一个Utf8串CONSTANT_Utf8,00 04(十进制4)表示长度, 43 6F 64 65是【Code】

第#10项 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65

01表示一个Utf8串CONSTANT_Utf8,00 0F(十进制15)表示长度, 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 是【LineNumberTable】

第#11项 01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65

01表示一个Utf8串CONSTANT_Utf8,00 12(十进制18) 表示长度, 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65是【LocalVariableTable】

第#12项 01 00 04 74 68 69 73

01表示一个Utf8串CONSTANT_Utf8,00 04(十进制4)表示长度,74 68 69 73 是【this】

第#13项 01 00 24 4C 70 75 62 2F 69 79 75 2F 73 65 2F 6A 76 6D 64 61 79 2F 64 61 79 30 33 2F 48 65 6C 6C 6F 57 6F 72 6C 64 3B

01表示一个Utf8串CONSTANT_Utf8,00 24(十进制36)表示长度,4C 70 75 62 2F 69 79 75 2F 73 65 2F 6A 76 6D 64 61 79 2F 64 61 79 30 33 2F 48 65 6C 6C 6F 57 6F 72 6C 64 3B是【Lpub/iyu/se/jvmday/day03/HelloWorld;】

第#14项 01 00 04 6D 61 69 6E

01表示一个Utf8串CONSTANT_Utf8,00 04(十进制4)表示长度,6D 61 69 6E是【main】

第#15项 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表示一个Utf8串CONSTANT_Utf8,00 16(十进制22)表示长度,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】其实就是参数为字符串数组,无返回值

第#16项 01 00 04 61 72 67 73

01表示一个Utf8串CONSTANT_Utf8,00 04(十进制4)表示长度,61 72 67 73是【args】

第#17项 01 00 13 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B

01表示一个Utf8串CONSTANT_Utf8,00 13(十进制19)表示长度,5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B是【Ljava/lang/String;】

第#18项 01 00 10 4D 65 74 68 6F 64 50 61 72 61 6D 65 74 65 72 73

01表示一个Utf8串CONSTANT_Utf8,00 10(十进制16)表示长度,4D 65 74 68 6F 64 50 61 72 61 6D 65 74 65 72 73是【MethodParameters】

第#19项 01 00 0A 53 6F 75 72 63 65 46 69 6C 65

01表示一个Utf8串CONSTANT_Utf8,00 0A(十进制10)表示长度,53 6F 75 72 63 65 46 69 6C 65是【SourceFile】

第#20项 01 00 0F 48 65 6C 6C 6F 57 6F 72 6C 64 2E 6A 61 76 61

01表示一个Utf8串CONSTANT_Utf8,00 0F(十进制15)表示长度,48 65 6C 6C 6F 57 6F 72 6C 64 2E 6A 61 76 61是【HelloWorld.java】

第#21项 0C 00 07 00 08

0C表示一个【名+类型】CONSTANT_NameAndType,00 07 00 08 引用了常量池中#7和#8两项

第#22项 07 00 1D

07表示一个Class信息CONSTANT_Class,00 1D(十进制29)表示它引用了常量池中#29项

第#23项 0C 00 1E 00 1F

0C表示一个【名+类型】CONSTANT_NameAndType,00 1E (十进制30)和 00 1F(十进制31)表示它引用了常量池中#30和#31两项

第#24项 01 00 0B 48 65 6C 6C 6F 20 77 6F 72 6C 64

01表示一个Utf8串CONSTANT_Utf8,00 0B(十进制11)表示长度,48 65 6C 6C 6F 20 77 6F 72 6C 64是【Hello world】

第#25项 07 00 20

07表示一个Class信息CONSTANT_Class,00 20(十进制32) 表示它引用了常量池中#32项

第#26项 0C 00 21 00 22

0C表示一个【名+类型】CONSTANT_NameAndType,00 21(十进制33) 和 00 22(十进制34)表示它引用了常量池中#33和#34两项

第#27项 01 00 22 70 75 62 2F 69 79 75 2F 73 65 2F 6A 76 6D 64 61 79 2F 64 61 79 30 33 2F 48 65 6C 6C 6F 57 6F 72 6C 64

01表示一个Utf8串CONSTANT_Utf8,00 22(十进制34)表示长度,70 75 62 2F 69 79 75 2F 73 65 2F 6A 76 6D 64 61 79 2F 64 61 79 30 33 2F 48 65 6C 6C 6F 57 6F 72 6C 64是【pub/iyu/se/jvmday/day03/HelloWorld】

第#28项 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74

01表示一个Utf8串CONSTANT_Utf8,00 10(十进制16)表示长度,6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74是【java/lang/Object】

第#29项 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D

01表示一个Utf8串CONSTANT_Utf8,00 10(十进制16)表示长度,6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D是【java/lang/System】

第#30项 01 00 03 6F 75 74

01表示一个Utf8串CONSTANT_Utf8,00 03(十进制3)表示长度,6F 75 74是【out】

第#31项 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表示一个Utf8串CONSTANT_Utf8,00 15(十进制21)表示长度,4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B是【Ljava/io/PrintStream;】

第#32项 01 00 13 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D

01表示一个Utf8串CONSTANT_Utf8,00 13(十进制19)表示长度,6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D是【java/io/PrintStream】

第#33项 01 00 07 70 72 69 6E 74 6C 6E

01表示一个Utf8串CONSTANT_Utf8,00 07(十进制7)表示长度,70 72 69 6E 74 6C 6E是【println】

第#34项 01 00 15 28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56

01表示一个Utf8串CONSTANT_Utf8,00 15(十进制21)表示长度,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】

1.4 访问标识与继承信息

1
2
3
4
5
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];

Field access and property flags:

Flag Name Value Interpretation
ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its package.
ACC_PRIVATE 0x0002 Declared private; usable only within the defining class.
ACC_PROTECTED 0x0004 Declared protected; may be accessed within subclasses.
ACC_STATIC 0x0008 Declared static.
ACC_FINAL 0x0010 Declared final; never directly assigned to after object construction (JLS §17.5).
ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.
ACC_TRANSIENT 0x0080 Declared transient; not written or read by a persistent object manager.
ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code.
ACC_ENUM 0x4000 Declared as an element of an enum.
1
000001c0h: 00 21 00 05 00 06 00 00 00 00 00 02 00 01 00 07

00 21 表示该class是一个类,公共的

00 05 表示根据常量池中#5找到本类全限定名

00 06 表示根据常量池中#6找到父类全限定名

00 00 表示接口的数量,本类为0

1.5 Field信息

1
2
u2          fields_count;
field_info fields[fields_count];
1
000001c0h: 00 21 00 05 00 06 00 00 00 00 00 02 00 01 00 07

00 00 表示成员变量数量,本类为0

FieldType term Type Interpretation
B byte signed byte
C char Unicode character code point in the Basic Multilingual Plane, encoded with UTF-16
D double double-precision floating-point value
F float single-precision floating-point value
I int integer
J long long integer
L ClassName ; reference an instance of class ClassName
S short signed short
Z boolean true or false
[ reference one array dimension

1.6 Method信息

1
2
u2           methods_count;
method_info methods[methods_count];

Method access and property flags:

Flag Name Value Interpretation
ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its package.
ACC_PRIVATE 0x0002 Declared private; accessible only within the defining class.
ACC_PROTECTED 0x0004 Declared protected; may be accessed within subclasses.
ACC_STATIC 0x0008 Declared static.
ACC_FINAL 0x0010 Declared final; must not be overridden (§5.4.5).
ACC_SYNCHRONIZED 0x0020 Declared synchronized; invocation is wrapped by a monitor use.
ACC_BRIDGE 0x0040 A bridge method, generated by the compiler.
ACC_VARARGS 0x0080 Declared with variable number of arguments.
ACC_NATIVE 0x0100 Declared native; implemented in a language other than Java.
ACC_ABSTRACT 0x0400 Declared abstract; no implementation is provided.
ACC_STRICT 0x0800 Declared strictfp; floating-point mode is FP-strict.
ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code.
1
2
3
4
5
000001c0h: 00 21 00 05 00 06 00 00 00 00 00 02 00 01 00 07
000001d0h: 00 08 00 01 00 09 00 00 00 2F 00 01 00 01 00 00
000001e0h: 00 05 2A B7 00 01 B1 00 00 00 02 00 0A 00 00 00
000001f0h: 06 00 01 00 00 00 09 00 0B 00 00 00 0C 00 01 00
00000200h: 00 00 05 00 0C 00 0D 00 00 00 09 00 0E 00 0F 00

00 02 表示方法数量,本类为2

一个方法由 访问修饰符 + 名称 + 参数描述 + 方法属性数量 + 方法属性 组成

  1. 00 01 代表访问修饰符(本类中是public)
  2. 00 07 代表引用了常量池#07项作为方法名称
  3. 00 08 代表引用了常量池#08项作为方法参数描述
  4. 00 01 代表方法属性数量,本方法是1
  5. 方法属性:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    1. 00 09 表示引用了常量池#09项,是【Code】属性
    2. 00 00 00 2F 表示此属性的长度是47
    3. 00 01 表示【操作数栈】最大深度
    4. 00 01 表示【局部变量表】最大槽数(slot)
    5. 00 00 00 05 表示字节码长度,本例是5
    6. 2A B7 00 01 B1 是字节码指令
    7. 00 00 00 02 表示方法细节属性数量,本例是2
    8. 00 0A 表示引用了常量池#10项,是【LineNumberTable】属性
    00 00 00 06 表示此属性的总长度,本例是6
    00 01 表示【LineNumberTable】长度
    00 00 表示【字节码】行号
    00 09 表示【java源码】行号
    9. 00 0B 表示引用了常量池#11项,是【LocalVariableTable】属性
    00 00 00 0C 表示此属性的总长度,本例是12
    00 01 表示【LocalVariableTable】长度
    00 00 表示局部变量生命周期开始,相当于字节码的偏移量
    00 05 表示局部变量覆盖的范围长度
    00 0C 表示局部变量名称,本例引用了常量池#12项,是【this
    00 0D 表示局部变量的类型,本例引用了常量池#13项,是【Lpub/iyu/se/jvmday/day03/HelloWorld;】
    00 00 表示局部变量占有的槽位(slot)编号,本例是0
1
2
3
4
5
6
00000200h: 00 00 05 00 0C 00 0D 00 00 00 09 00 0E 00 0F 00
00000210h: 02 00 09 00 00 00 37 00 02 00 01 00 00 00 09 B2
00000220h: 00 02 12 03 B6 00 04 B1 00 00 00 02 00 0A 00 00
00000230h: 00 0A 00 02 00 00 00 0B 00 08 00 0C 00 0B 00 00
00000240h: 00 0C 00 01 00 00 00 09 00 10 00 11 00 00 00 12
00000250h: 00 00 00 05 01 00 10 00 00 00 01 00 13 00 00 00
  1. 00 09 代表访问修饰符(本类中是public static)
  2. 00 0E 代表引用了常量池#14项作为方法名称
  3. 00 0F 代表引用了常量池#15项作为方法参数描述
  4. 00 02 代表方法属性数量,本方法是2
  5. 方法属性1:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    00 09 表示引用了常量池#09项,是【Code】属性
    00 00 00 37 表示此属性的长度是55
    00 02 表示【操作数栈】最大深度
    00 01 表示【局部变量表】最大槽数(slot)
    00 00 00 09 表示字节码长度,本例是9
    B2 00 02 12 03 B6 00 04 B1 是字节码指令
    00 00 00 02 表示方法细节属性数量,本例是2
    00 0A 表示引用了常量池#10项,是【LineNumberTable】属性
    00 00 00 0A 表示此属性的总长度,本例是10
    00 02 表示【LineNumberTable】长度
    00 00 表示【字节码】行号
    00 0B 表示【java源码】行号
    00 08 表示【字节码】行号
    00 0C 表示【java源码】行号
    00 0B 表示引用了常量池#11项,是【LocalVariableTable】属性
    00 00 00 0C 表示此属性的总长度,本例是12
    00 01 表示【LocalVariableTable】长度
    00 00 表示局部变量生命周期开始,相当于字节码的偏移量
    00 09 表示局部变量覆盖的范围长度
    00 10 表示局部变量名称,本例引用了常量池#16项,是【args】
    00 11 表示局部变量的类型,本例引用了常量池#17项,是【Ljava/lang/String;】
    00 00 表示局部变量占有的槽位(slot)编号,本例是0
  6. 方法属性2:
    1
    2
    3
    4
    5
    00 12 表示引用了常量池#18项,发现是【MethodParameters】属性
    00 00 00 05 表示此属性的总长度,本例是5
    01 参数数量
    00 10 表示引用了常量池#16项,是【args】
    00 00 表示访问修饰符

1.7 附加属性

1
2
u2             attributes_count;
attribute_info attributes[attributes_count];
1
2
00000250h: 00 00 00 05 01 00 10 00 00 00 01 00 13 00 00 00
00000260h: 02 00 14
1
2
3
4
00 01 表示附加属性数量
00 13 表示引用了常量池#19项,即【SourceFile】
00 00 00 02 表示此属性的长度
00 14 表示引用了常量池#20项,即【HelloWorld.java】

参考资料:点击打开

2.字节码指令

2.1 分析

根据上面的分析,有两组字节码指令:

(1)public pub.iyu.se.jvmday.day03.HelloWorld(); 构造方法的字节码指令

2A B7 00 01 B1 

1.2A => aload_0加载slot()的局部变量,即this,作为下面的invokespecial构造方法调用的参数
2.B7 => invokespecial预备调用构造方法,哪个方法呢?
3.00 01 => 引用常量池中#1项,即【Method java/lang/Object."<init>":()v】
4.B1 => 表示返回

(2)public static void main(java.lang.String[]) 主方法的字节码指令

B2 00 02 12 03 B6 00 04 B1 

1.B2 => getstatic用来加载静态变量,哪个静态变量呢?
2.00 02 => 引用常量池中#2项,即【Field java/lang/System.out:Ljava/io/PrintStream;】
3.12 => ldc加载参数,哪个参数呢?
4.03 => 引用常量池中#3项,即【String Hello world】
5.B6 => invokevirtual预备调用成员方法,哪个方法呢?
6.00 04 => 引用常量池中#4项,即【Method java/io/PrintStream.println:(Ljava/lang/String;)V】
7.B1 => 表示返回

参考资料:点击打开

2.2 javap工具

Oracle 提供javap工具来反编译class文件

1
javap -v HelloWorld.class

结果如下:

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
Classfile /E:/HelloWorld.class
Last modified 2020-11-20; size 611 bytes
MD5 checksum 93d2a7c13284af1a96716e531e2f45e4
Compiled from "HelloWorld.java"
public class pub.iyu.se.jvmday.day03.HelloWorld
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#21 // java/lang/Object."<init>":()V
#2 = Fieldref #22.#23 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #24 // Hello world
#4 = Methodref #25.#26 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #27 // pub/iyu/se/jvmday/day03/HelloWorld
#6 = Class #28 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lpub/iyu/se/jvmday/day03/HelloWorld;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 [Ljava/lang/String;
#18 = Utf8 MethodParameters
#19 = Utf8 SourceFile
#20 = Utf8 HelloWorld.java
#21 = NameAndType #7:#8 // "<init>":()V
#22 = Class #29 // java/lang/System
#23 = NameAndType #30:#31 // out:Ljava/io/PrintStream;
#24 = Utf8 Hello world
#25 = Class #32 // java/io/PrintStream
#26 = NameAndType #33:#34 // println:(Ljava/lang/String;)V
#27 = Utf8 pub/iyu/se/jvmday/day03/HelloWorld
#28 = Utf8 java/lang/Object
#29 = Utf8 java/lang/System
#30 = Utf8 out
#31 = Utf8 Ljava/io/PrintStream;
#32 = Utf8 java/io/PrintStream
#33 = Utf8 println
#34 = Utf8 (Ljava/lang/String;)V
{
public pub.iyu.se.jvmday.day03.HelloWorld();
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 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lpub/iyu/se/jvmday/day03/HelloWorld;

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 11: 0
line 12: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args [Ljava/lang/String;
MethodParameters:
Name Flags
args
}
SourceFile: "HelloWorld.java"

2.3 方法执行流程

(1) Demo代码如下:

1
2
3
4
5
6
7
8
public class TestDemo0301 {
public static void main(String[] args) {
int a = 10;
int b = Short.MAX_VALUE + 1;
int c = a + b;
System.out.println(c);
}
}

(2)编译后的字节码文件 javap -v TestDemo0301.class

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
Classfile /E:/TestDemo0301.class
Last modified 2020-11-23; size 676 bytes
MD5 checksum 0292b59b6c598154c1fa633b3ccb3c74
Compiled from "TestDemo0301.java"
public class pub.iyu.se.jvmday.day03.TestDemo0301
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #7.#26 // java/lang/Object."<init>":()V
#2 = Class #27 // java/lang/Short
#3 = Integer 32768
#4 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
#5 = Methodref #30.#31 // java/io/PrintStream.println:(I)V
#6 = Class #32 // pub/iyu/se/jvmday/day03/TestDemo0301
#7 = Class #33 // java/lang/Object
#8 = Utf8 <init>
#9 = Utf8 ()V
#10 = Utf8 Code
#11 = Utf8 LineNumberTable
#12 = Utf8 LocalVariableTable
#13 = Utf8 this
#14 = Utf8 Lpub/iyu/se/jvmday/day03/TestDemo0301;
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 args
#18 = Utf8 [Ljava/lang/String;
#19 = Utf8 a
#20 = Utf8 I
#21 = Utf8 b
#22 = Utf8 c
#23 = Utf8 MethodParameters
#24 = Utf8 SourceFile
#25 = Utf8 TestDemo0301.java
#26 = NameAndType #8:#9 // "<init>":()V
#27 = Utf8 java/lang/Short
#28 = Class #34 // java/lang/System
#29 = NameAndType #35:#36 // out:Ljava/io/PrintStream;
#30 = Class #37 // java/io/PrintStream
#31 = NameAndType #38:#39 // println:(I)V
#32 = Utf8 pub/iyu/se/jvmday/day03/TestDemo0301
#33 = Utf8 java/lang/Object
#34 = Utf8 java/lang/System
#35 = Utf8 out
#36 = Utf8 Ljava/io/PrintStream;
#37 = Utf8 java/io/PrintStream
#38 = Utf8 println
#39 = Utf8 (I)V
{
public pub.iyu.se.jvmday.day03.TestDemo0301();
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 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lpub/iyu/se/jvmday/day03/TestDemo0301;

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: bipush 10
2: istore_1
3: ldc #3 // int 32768
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: istore_3
10: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
13: iload_3
14: invokevirtual #5 // Method java/io/PrintStream.println:(I)V
17: return
LineNumberTable:
line 11: 0
line 12: 3
line 13: 6
line 14: 10
line 15: 17
LocalVariableTable:
Start Length Slot Name Signature
0 18 0 args [Ljava/lang/String;
3 15 1 a I
6 12 2 b I
10 8 3 c I
MethodParameters:
Name Flags
args
}
SourceFile: "TestDemo0301.java"

(3)常量池 载入 运行时常量池

JVM-day03-03.png

(4)方法字节码载入方法区

JVM-day03-04.png

(5)main线程开始运行,分配栈帧内存

stack=2(操作数栈), locals=4(局部变量表)

JVM-day03-05.png

(6)执行引擎开始执行字节码

bipush 10

将一个byte压入操作数栈(其长度会补齐4个字节)

类似的指令还有:

1. sipush 将一个short压入操作数栈(其长度会补齐4个字节)
2. ldc将一个int压入操作数栈
3. ldc2_w将一个long压入操作数栈(分两次压入,因为long是8个字节)

这里小的数字都是和字节码指令存在一起,超过short范围的数字存入了常量池

JVM-day03-06.png

istore 1

将操作数栈顶数据弹出,存入局部变量表的slot 1

JVM-day03-07.png

JVM-day03-08.png

ldc #3

从常量池加载#3数据到操作数栈

注意:Short.MAX_VALUE是32767,所以32768=Short.MAX_VALUE + 1 实际是在编译期间计算好的

JVM-day03-09.png

istore 2

JVM-day03-10.png

JVM-day03-11.png

iload 1

JVM-day03-12.png

iload 2

JVM-day03-13.png

iadd

JVM-day03-14.png

JVM-day03-15.png

istore 3

JVM-day03-16.png

JVM-day03-17.png

getstatic #4

JVM-day03-18.png

JVM-day03-19.png

iload 3

JVM-day03-20.png

JVM-day03-21.png

invokevirtual #5

  1. 找到常量池#5项
  2. 定位到方法区 java/io/PrintStream.println:(I)V方法
  3. 生成新的栈帧(分配locals、stack等)
  4. 传递参数,执行新栈帧中的字节码

JVM-day03-22.png

执行完毕,弹出栈帧

清除main操作数栈内容

JVM-day03-23.png

return

完成main方法调用,弹出main栈帧,程序结束

2.4 分析 i++

从字节码的角度分析 i++ 相关问题

源码如下:

1
2
3
4
5
6
7
8
public class TestDemo0302 {
public static void main(String[] args) {
int a = 10;
int b = a++ + ++a + a--;
System.out.println(a);
System.out.println(b);
}
}

字节码:

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
Classfile /E:/TestDemo0302.class
Last modified 2020-11-24; size 651 bytes
MD5 checksum 46b35f856d1e5581c28225b908080dc0
Compiled from "TestDemo0302.java"
public class pub.iyu.se.jvmday.day03.TestDemo0302
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#23 // java/lang/Object."<init>":()V
#2 = Fieldref #24.#25 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #26.#27 // java/io/PrintStream.println:(I)V
#4 = Class #28 // pub/iyu/se/jvmday/day03/TestDemo0302
#5 = Class #29 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 LocalVariableTable
#11 = Utf8 this
#12 = Utf8 Lpub/iyu/se/jvmday/day03/TestDemo0302;
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 args
#16 = Utf8 [Ljava/lang/String;
#17 = Utf8 a
#18 = Utf8 I
#19 = Utf8 b
#20 = Utf8 MethodParameters
#21 = Utf8 SourceFile
#22 = Utf8 TestDemo0302.java
#23 = NameAndType #6:#7 // "<init>":()V
#24 = Class #30 // java/lang/System
#25 = NameAndType #31:#32 // out:Ljava/io/PrintStream;
#26 = Class #33 // java/io/PrintStream
#27 = NameAndType #34:#35 // println:(I)V
#28 = Utf8 pub/iyu/se/jvmday/day03/TestDemo0302
#29 = Utf8 java/lang/Object
#30 = Utf8 java/lang/System
#31 = Utf8 out
#32 = Utf8 Ljava/io/PrintStream;
#33 = Utf8 java/io/PrintStream
#34 = Utf8 println
#35 = Utf8 (I)V
{
public pub.iyu.se.jvmday.day03.TestDemo0302();
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 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lpub/iyu/se/jvmday/day03/TestDemo0302;

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=1
0: bipush 10
2: istore_1
3: iload_1
4: iinc 1, 1
7: iinc 1, 1
10: iload_1
11: iadd
12: iload_1
13: iinc 1, -1
16: iadd
17: istore_2
18: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
21: iload_1
22: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
25: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
28: iload_2
29: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
32: return
LineNumberTable:
line 11: 0
line 12: 3
line 13: 18
line 14: 25
line 15: 32
LocalVariableTable:
Start Length Slot Name Signature
0 33 0 args [Ljava/lang/String;
3 30 1 a I
18 15 2 b I
MethodParameters:
Name Flags
args
}
SourceFile: "TestDemo0302.java"

分析:

  1. 注意iinc指令是直接在局部变量slot上进行运算
  2. a++ 和++a的区别是先执行iload还是先执行iinc
    (1)a++ 先执行iload,再执行iinc
    (2)++a 先执行iinc,再执行iload

2.5 条件判断指令

JVM-day03-24.png

说明:
1. byte,short,char 都会按int比较,因为操作数栈都是4字节
2. goto 用来进行跳转到指定行号的字节码

源码:

1
2
3
4
5
6
7
8
public static void main(String[] args) {
int a = 0;
if(a == 0){
a = 10;
}else {
a = 20;
}
}

字节码:

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
Classfile /E:/TestDemo0303.class
Last modified 2020-12-10; size 535 bytes
MD5 checksum 0a17c88bcb86b283e848b2a38d73bce6
Compiled from "TestDemo0303.java"
public class pub.iyu.se.jvmday.day03.TestDemo0303
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#21 // java/lang/Object."<init>":()V
#2 = Class #22 // pub/iyu/se/jvmday/day03/TestDemo0303
#3 = Class #23 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 Lpub/iyu/se/jvmday/day03/TestDemo0303;
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 args
#14 = Utf8 [Ljava/lang/String;
#15 = Utf8 a
#16 = Utf8 I
#17 = Utf8 StackMapTable
#18 = Utf8 MethodParameters
#19 = Utf8 SourceFile
#20 = Utf8 TestDemo0303.java
#21 = NameAndType #4:#5 // "<init>":()V
#22 = Utf8 pub/iyu/se/jvmday/day03/TestDemo0303
#23 = Utf8 java/lang/Object
{
public pub.iyu.se.jvmday.day03.TestDemo0303();
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 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lpub/iyu/se/jvmday/day03/TestDemo0303;

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: ifne 12
6: bipush 10
8: istore_1
9: goto 15
12: bipush 20
14: istore_1
15: return
LineNumberTable:
line 11: 0
line 12: 2
line 13: 6
line 15: 12
line 17: 15
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 args [Ljava/lang/String;
2 14 1 a I
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 12
locals = [ int ]
frame_type = 2 /* same */
MethodParameters:
Name Flags
args
}
SourceFile: "TestDemo0303.java"

关于比较指令中 long float double 的比较,可以参考资料

2.6 循环控制指令

while循环:

1
2
3
4
5
6
public static void main(String[] args) {
int a = 0;
while(a < 0){
a++;
}
}

字节码:

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
Classfile /E:/TestDemo0304.class
Last modified 2020-12-10; size 530 bytes
MD5 checksum 016d8b924c6a38e2d2f72ab9734593a3
Compiled from "TestDemo0304.java"
public class pub.iyu.se.jvmday.day03.TestDemo0304
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#21 // java/lang/Object."<init>":()V
#2 = Class #22 // pub/iyu/se/jvmday/day03/TestDemo0304
#3 = Class #23 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 Lpub/iyu/se/jvmday/day03/TestDemo0304;
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 args
#14 = Utf8 [Ljava/lang/String;
#15 = Utf8 a
#16 = Utf8 I
#17 = Utf8 StackMapTable
#18 = Utf8 MethodParameters
#19 = Utf8 SourceFile
#20 = Utf8 TestDemo0304.java
#21 = NameAndType #4:#5 // "<init>":()V
#22 = Utf8 pub/iyu/se/jvmday/day03/TestDemo0304
#23 = Utf8 java/lang/Object
{
public pub.iyu.se.jvmday.day03.TestDemo0304();
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 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lpub/iyu/se/jvmday/day03/TestDemo0304;

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 14
8: iinc 1, 1
11: goto 2
14: return
LineNumberTable:
line 11: 0
line 12: 2
line 13: 8
line 15: 14
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 args [Ljava/lang/String;
2 13 1 a I
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 2
locals = [ int ]
frame_type = 11 /* same */
MethodParameters:
Name Flags
args
}
SourceFile: "TestDemo0304.java"

do while循环:

1
2
3
4
5
6
public static void main(String[] args) {
int a = 0;
do {
a++;
} while(a < 10);
}

字节码:

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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
Classfile /E:/TestDemo0305.class
Last modified 2020-12-10; size 526 bytes
MD5 checksum 100b9f049f784b67731ee8c992011082
Compiled from "TestDemo0305.java"
public class pub.iyu.se.jvmday.day03.TestDemo0305
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#21 // java/lang/Object."<init>":()V
#2 = Class #22 // pub/iyu/se/jvmday/day03/TestDemo0305
#3 = Class #23 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = Utf8 LineNumberTable
#8 = Utf8 LocalVariableTable
#9 = Utf8 this
#10 = Utf8 Lpub/iyu/se/jvmday/day03/TestDemo0305;
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 args
#14 = Utf8 [Ljava/lang/String;
#15 = Utf8 a
#16 = Utf8 I
#17 = Utf8 StackMapTable
#18 = Utf8 MethodParameters
#19 = Utf8 SourceFile
#20 = Utf8 TestDemo0305.java
#21 = NameAndType #4:#5 // "<init>":()V
#22 = Utf8 pub/iyu/se/jvmday/day03/TestDemo0305
#23 = Utf8 java/lang/Object
{
public pub.iyu.se.jvmday.day03.TestDemo0305();
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 9: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lpub/iyu/se/jvmday/day03/TestDemo0305;

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iinc 1, 1
5: iload_1
6: bipush 10
8: if_icmplt 2
11: return
LineNumberTable:
line 11: 0
line 13: 2
line 14: 5
line 15: 11
LocalVariableTable:
Start Length Slot Name Signature
0 12 0 args [Ljava/lang/String;
2 10 1 a I
StackMapTable: number_of_entries = 1
frame_type = 252 /* append */
offset_delta = 2
locals = [ int ]
MethodParameters:
Name Flags
args
}
SourceFile: "TestDemo0305.java"

2.7 判断结果

2.8 构造方法

2.9 方法调用

2.10 多态的原理

2.11 异常处理

2.12 finally

2.13 synchronized

3.编译期处理

4.类加载阶段

5.类加载器

6.运行期优化

未完待续。。。

评论加载中