深入解析“Java 字节码 ” 之 「从案例深度解读 Java 字节码」
本文主要是《深入探索 JVM》系列中『字节码篇』文章,主要通过案例分析的形势,来更直观的认识 Java 字节码。系列文章目录见:《 深入探索 JVM 》文集
『字节码』篇文章推荐:
深入解析“Java 字节码 ” 之 「类文件结构」
深入解析“Java 字节码 ” 之 「从案例深度解读 Java 字节码」
深入解析“Java 字节码 ” 之 「进一步探究 Java 方法的字节码实现」
深入解析“Java 字节码 ” 之 「从案例解读虚拟机属性表」
深入解析“Java 字节码 ” 之 「虚拟机字节码执行引擎」
深入解析“Java 字节码 ” 之 「动态代理的实现」
实例代码
public class MyTest2 {
String str = "Welcome";
private int x = 5;
public static Integer in = 10;
public static void main(String[] args) {
MyTest2 myTest2 = new MyTest2();
myTest2.setX(8);
in = 20;
}
public void setX(int x) {
this.x = x;
}
}
# -p -private Show all classes and members
javap -verbose -p MyTest2
👆 -p 才会显示私有的成员(属性、方法、类等)的信息
➜ main git:(master) javap -p -v com.bayern.shengsiyuan.jvm_lecture.bytecode.lesson13.MyTest2
Classfile /Users/hayashishaochie/Documents/IdeaWorkspace/shengshiyuanProject/jvm_lecture/build/classes/main/com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.class
Last modified 2019-9-1; size 903 bytes
MD5 checksum 44a9abb1b347cded1f441fbc8e1b7a7a
Compiled from "MyTest2.java"
public class com.bayern.shengsiyuan.jvm_lecture.bytecode.lesson13.MyTest2
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #10.#34 // java/lang/Object."<init>":()V
#2 = String #35 // Welcome
#3 = Fieldref #5.#36 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.str:Ljava/lang/String;
#4 = Fieldref #5.#37 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.x:I
#5 = Class #38 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2
#6 = Methodref #5.#34 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2."<init>":()V
#7 = Methodref #5.#39 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.setX:(I)V
#8 = Methodref #40.#41 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#9 = Fieldref #5.#42 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.in:Ljava/lang/Integer;
#10 = Class #43 // java/lang/Object
#11 = Utf8 str
#12 = Utf8 Ljava/lang/String;
#13 = Utf8 x
#14 = Utf8 I
#15 = Utf8 in
#16 = Utf8 Ljava/lang/Integer;
#17 = Utf8 <init>
#18 = Utf8 ()V
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 LocalVariableTable
#22 = Utf8 this
#23 = Utf8 Lcom/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2;
#24 = Utf8 main
#25 = Utf8 ([Ljava/lang/String;)V
#26 = Utf8 args
#27 = Utf8 [Ljava/lang/String;
#28 = Utf8 myTest2
#29 = Utf8 setX
#30 = Utf8 (I)V
#31 = Utf8 <clinit> // 类构造器,对类的静态变量、静态代码块进行初始化;如果没有需要初始化的数据,则不会有<clinit>方法
#32 = Utf8 SourceFile
#33 = Utf8 MyTest2.java
#34 = NameAndType #17:#18 // "<init>":()V
#35 = Utf8 Welcome
#36 = NameAndType #11:#12 // str:Ljava/lang/String;
#37 = NameAndType #13:#14 // x:I
#38 = Utf8 com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2
#39 = NameAndType #29:#30 // setX:(I)V
#40 = Class #44 // java/lang/Integer
#41 = NameAndType #45:#46 // valueOf:(I)Ljava/lang/Integer;
#42 = NameAndType #15:#16 // in:Ljava/lang/Integer;
#43 = Utf8 java/lang/Object
#44 = Utf8 java/lang/Integer
#45 = Utf8 valueOf
#46 = Utf8 (I)Ljava/lang/Integer;
{
java.lang.String str;
descriptor: Ljava/lang/String;
flags:
private int x;
descriptor: I
flags: ACC_PRIVATE
public static java.lang.Integer in;
descriptor: Ljava/lang/Integer;
flags: ACC_PUBLIC, ACC_STATIC
public com.bayern.shengsiyuan.jvm_lecture.bytecode.lesson13.MyTest2();
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: ldc #2 // String Welcome
7: putfield #3 // Field str:Ljava/lang/String;
10: aload_0
11: iconst_5
12: putfield #4 // Field x:I
15: return
LineNumberTable:
line 3: 0
line 4: 4
line 6: 10
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 this Lcom/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2;
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: new #5 // class com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2
3: dup
4: invokespecial #6 // Method "<init>":()V
7: astore_1
8: aload_1
9: bipush 8
11: invokevirtual #7 // Method setX:(I)V
14: bipush 20
16: invokestatic #8 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 自动装箱的过程;对应源码「in = 20;」
19: putstatic #9 // Field in:Ljava/lang/Integer;
22: return
LineNumberTable:
line 11: 0
line 13: 8
line 15: 14
line 16: 22
LocalVariableTable:
Start Length Slot Name Signature
0 23 0 args [Ljava/lang/String;
8 15 1 myTest2 Lcom/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2;
public void setX(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: putfield #4 // Field x:I
5: return
LineNumberTable:
line 19: 0
line 20: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2;
0 6 1 x I
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: bipush 10
2: invokestatic #8 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
5: putstatic #9 // Field in:Ljava/lang/Integer;
8: return
LineNumberTable:
line 8: 0
}
SourceFile: "MyTest2.java"
十六进制字节码
CAFE BABE 0000 0034 002F 0A00 0A00 2208 0023 0900 0500 2409 0005 0025 0700 260A 0005 0022 0A00 0500 270A 0028 0029 0900 0500 2A07 002B 0100 0373 7472 0100 124C 6A61 7661 2F6C 616E 672F 5374 7269 6E67 3B01 0001 7801 0001 4901 0002 696E 0100 134C 6A61 7661 2F6C 616E 672F 496E 7465 6765 723B 0100 063C 696E 6974 3E01 0003 2829 5601 0004 436F 6465 0100 0F4C 696E 654E 756D 6265 7254 6162 6C65 0100 124C 6F63 616C 5661 7269 6162 6C65 5461 626C 6501 0004 7468 6973 0100 3E4C 636F 6D2F 6261 7965 726E 2F73 6865 6E67 7369 7975 616E 2F6A 766D 5F6C 6563 7475 7265 2F62 7974 6563 6F64 652F 6C65 7373 6F6E 3133 2F4D 7954 6573 7432 3B01 0004 6D61 696E 0100 1628 5B4C 6A61 7661 2F6C 616E 672F 5374 7269 6E67 3B29 5601 0004 6172 6773 0100 135B 4C6A 6176 612F 6C61 6E67 2F53 7472 696E 673B 0100 076D 7954 6573 7432 0100 0473 6574 5801 0004 2849 2956 0100 083C 636C 696E 6974 3E01 000A 536F 7572 6365 4669 6C65 0100 0C4D 7954 6573 7432 2E6A 6176 610C 0011 0012 0100 0757 656C 636F 6D65 0C00 0B00 0C0C 000D 000E 0100 3C63 6F6D 2F62 6179 6572 6E2F 7368 656E 6773 6979 7561 6E2F 6A76 6D5F 6C65 6374 7572 652F 6279 7465 636F 6465 2F6C 6573 736F 6E31 332F 4D79 5465 7374 320C 001D 001E 0700 2C0C 002D 002E 0C00 0F00 1001 0010 6A61 7661 2F6C 616E 672F 4F62 6A65 6374 0100 116A 6176 612F 6C61 6E67 2F49 6E74 6567 6572 0100 0776 616C 7565 4F66 0100 1628 4929 4C6A 6176 612F 6C61 6E67 2F49 6E74 6567 6572 3B00 2100 0500 0A00 0000 0300 0000 0B00 0C00 0000 0200 0D00 0E00 0000 0900 0F00 1000 0000 0400 0100 1100 1200 0100 1300 0000 4200 0200 0100 0000 102A B700 012A 1202 B500 032A 08B5 0004 B100 0000 0200 1400 0000 0E00 0300 0000 0300 0400 0400 0A00 0600 1500 0000 0C00 0100 0000 1000 1600 1700 0000 0900 1800 1900 0100 1300 0000 5700 0200 0200 0000 17BB 0005 59B7 0006 4C2B 1008 B600 0710 14B8 0008 B300 09B1 0000 0002 0014 0000 0012 0004 0000 000B 0008 000D 000E 000F 0016 0010 0015 0000 0016 0002 0000 0017 001A 001B 0000 0008 000F 001C 0017 0001 0001 001D 001E 0001 0013 0000 003E 0002 0002 0000 0006 2A1B B500 04B1 0000 0002 0014 0000 000A 0002 0000 0013 0005 0014 0015 0000 0016 0002 0000 0006 0016 0017 0000 0000 0006 000D 000E 0001 0008 001F 0012 0001 0013 0000 0021 0001 0000 0000 0009 100A B800 08B3 0009 B100 0000 0100 1400 0000 0600 0100 0000 0800 0100 2000 0000 0200 21
一,magic ——— 魔数
格式:
u4 magic;
内容:
CAFE BABE
二,版本号
2.1 minor_version ——— 次版本号
格式:
u2 minor_version
内容:
0000 // 次版本号:0
2.2 major_version ———— 主版本号
格式:
u2 major_version
内容:
0034 // 主版本号:0x34 -> 52(十进制) ———— 52 —— jdk8
三,常量池
3.1 constant_pool_count ———— 常量个数
格式:
u2 constant_pool_count
内容:
00 2F // 常量个数:0x002F -> 47(十进制) ———— 47 个常量
3.2 constant_pool ———— 常量表
格式:
cp_info constant_pool[constant_pool_count - 1]; // cp_info 数组的个数为 46
- 第一个常量
# 结构
CONSTANT_Methodref_info {
u1 tag
u2 index // 指向声明方法的类描述福 CONSTANT_Class_info 的索引值
u2 index // 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项
}
0A ———— CONSTANT_Methodref_info
00 0A ———— CONSTANT_Class_info 索引 :10
00 22 ———— CONSTANT_NameAndType_info :34
#1 = Methodref #10.#34 // java/lang/Object."<init>":()V
- 第二个常量
# 结构
CONSTANT_String_info {
u1 tag
u2 index // 指向字符串字面量的索引
}
08 ———— CONSTANT_String_info
00 23 ———— 指向字符串字面量的索引:35
#2 = String #35 // Welcome
- 第三个常量
# 结构
CONSTANT_Fieldref_info {
u1 tag
u2 index // 指向声明字段的类或接口描述符 CONSTANT_Class_info 的索引项
u2 index // 指向字段描述符 CONSTANT_NameAndType_info 的索引项
}
09 ———— CONSTANT_Fieldref_info
00 05 ———— CONSTANT_Class_info 的索引项:5
00 24 ———— CONSTANT_NameAndType_info 的索引项:36
#3 = Fieldref #5.#36 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.str:Ljava/lang/String;
- 第四个常量
# 结构
CONSTANT_Fieldref_info {
u1 tag
u2 index // 指向声明字段的类或接口描述符 CONSTANT_Class_info 的索引项
u2 index // 指向字段描述符 CONSTANT_NameAndType_info 的索引项
}
09 ———— CONSTANT_Fieldref_info
00 05 ———— CONSTANT_Class_info 的索引项:5
00 25 ———— CONSTANT_NameAndType_info 的索引项:37
#4 = Fieldref #5.#37 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.x:I
- 第五个常量
# 结构
CONSTANT_Class_info {
u1 tag
u2 index // 指向全限定名常量项的索引
}
07 ———— CONSTANT_Class_info
00 26 ———— 全限定名常量项的索引:38
#5 = Class #38 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2
- 第六个常量
# 结构
CONSTANT_Methodref_info {
u1 tag
u2 index // 指向声明方法的类描述福 CONSTANT_Class_info 的索引值
u2 index // 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项
}
0A ———— CONSTANT_Methodref_info
00 05 ———— CONSTANT_Class_info 的索引值:5
00 22 ———— CONSTANT_NameAndType_info 的索引项:34
#6 = Methodref #5.#34 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2."<init>":()V
- 第七个常量
# 结构
CONSTANT_Methodref_info {
u1 tag
u2 index // 指向声明方法的类描述福 CONSTANT_Class_info 的索引值
u2 index // 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项
}
0A ———— CONSTANT_Methodref_info
00 05 ———— CONSTANT_Class_info 的索引值:5
00 27 ———— CONSTANT_NameAndType_info 的索引项:39
#7 = Methodref #5.#39 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.setX:(I)V
- 第八个常量
# 结构
CONSTANT_Methodref_info {
u1 tag
u2 index // 指向声明方法的类描述福 CONSTANT_Class_info 的索引值
u2 index // 指向名称及类型描述符 CONSTANT_NameAndType_info 的索引项
}
0A ———— CONSTANT_Methodref_info
00 28 ———— CONSTANT_Class_info 的索引值:40
00 29 ———— CONSTANT_NameAndType_info 的索引项:41
#8 = Methodref #40.#41 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
- 第九个常量
# 结构
CONSTANT_Fieldref_info {
u1 tag
u2 index // 指向声明字段的类或接口描述符 CONSTANT_Class_info 的索引项
u2 index // 指向字段描述符 CONSTANT_NameAndType_info 的索引项
}
09 ———— CONSTANT_Fieldref_info
00 05 ———— CONSTANT_Class_info 的索引项:5
00 2A ———— CONSTANT_NameAndType_info 的索引项:42
#9 = Fieldref #5.#42 // com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.in:Ljava/lang/Integer;
- 第十个常量
# 结构
CONSTANT_Class_info {
u1 tag
u2 index // 指向全限定名常量项的索引
}
07 ———— CONSTANT_Class_info
00 2B ———— 全限定名常量项的索引:43
#10 = Class #43 // java/lang/Object
- 第十一个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 03 ———— UTF-8 编码的字符串长度:3
73 74 72 ———— str
#11 = Utf8 str
- 第十二个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 12 ———— UTF-8 编码的字符串长度:18
4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B ———— Ljava/lang/String;
#12 = Utf8 Ljava/lang/String;
- 第十三个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 01 ———— UTF-8 编码的字符串长度:1
78 ———— x
#13 = Utf8 x
- 第十四个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 01 ———— UTF-8 编码的字符串长度:1
49 ———— I
#14 = Utf8 I
- 第十五个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 02 ———— UTF-8 编码的字符串长度:2
69 6E ———— in
#15 = Utf8 in
- 第十六个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 13 ———— UTF-8 编码的字符串长度:19
4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B ———— Ljava/lang/Integer;
#16 = Utf8 Ljava/lang/Integer;
- 第十七个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 06 ———— UTF-8 编码的字符串长度:6
3C 69 6E 69 74 3E ———— <init>
#17 = Utf8 <init>
- 第十八个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 03 ———— UTF-8 编码的字符串长度:3
28 29 56 ———— ()V
#18 = Utf8 ()V
- 第十九个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 04 ———— UTF-8 编码的字符串长度:4
43 6F 64 65 ———— Code
#19 = Utf8 Code
- 第二十个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 0F ———— UTF-8 编码的字符串长度:15
4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 ———— LineNumberTable
#20 = Utf8 LineNumberTable
- 第二十一个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 12 ———— UTF-8 编码的字符串长度:18
4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 ———— LocalVariableTable
#21 = Utf8 LocalVariableTable
- 第二十二个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 04 ———— UTF-8 编码的字符串长度:4
74 68 69 73 ———— this
#22 = Utf8 this
- 第二十三个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 3E ———— UTF-8 编码的字符串长度:62
4C 63 6F 6D 2F 62 61 79 65 72 6E 2F 73 68 65 6E 67 73 69 79 75 61 6E 2F 6A 76 6D 5F 6C 65 63 74 75 72 65 2F 62 79 74 65 63 6F 64 65 2F 6C 65 73 73 6F 6E 31 33 2F 4D 79 54 65 73 74 32 3B ———— Lcom/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2;
#23 = Utf8 Lcom/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2;
- 第二十四个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 04 ———— UTF-8 编码的字符串长度:4
6D 61 69 6E ———— main
#24 = Utf8 main
- 第二十五个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 16 ———— UTF-8 编码的字符串长度:22
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
#25 = Utf8 ([Ljava/lang/String;)V
- 第二十六个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 04 ———— UTF-8 编码的字符串长度:4
61 72 67 73 ———— args
#26 = Utf8 args
- 第二十七个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 13 ———— UTF-8 编码的字符串长度:19
5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B ———— [Ljava/lang/String;
#27 = Utf8 [Ljava/lang/String;
- 第二十八个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 07 ———— UTF-8 编码的字符串长度:7
6D 79 54 65 73 74 32 ———— myTest2
#28 = Utf8 myTest2
- 第二十九个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 04 ———— UTF-8 编码的字符串长度:4
73 65 74 58 ———— setX
#29 = Utf8 setX
- 第三十个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 04 ———— UTF-8 编码的字符串长度:4
28 49 29 56 ———— (I)V
#30 = Utf8 (I)V
- 第三十一个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 08 ———— UTF-8 编码的字符串长度:8
3C 63 6C 69 6E 69 74 3E ———— <clinit>
#31 = Utf8 <clinit>
- 第三十二个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 0A ———— UTF-8 编码的字符串长度:10
53 6F 75 72 63 65 46 69 6C 65 ———— SourceFile
#32 = Utf8 SourceFile
- 第三十三个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 0C ———— UTF-8 编码的字符串长度:12
4D 79 54 65 73 74 32 2E 6A 61 76 61 ———— MyTest2.java
#33 = Utf8 MyTest2.java
- 第三十四个常量
# 结构
CONSTANT_NameAndType_info {
u1 tag
u2 index // 指向该字段或方法名称常量项的索引
u2 index // 指向该字段或方法描述符常量项的索引
}
0C ———— CONSTANT_NameAndType_info
00 11 ———— 该字段或方法名称常量项的索引:17
00 12 ———— 该字段或方法描述符常量项的索引:18
#34 = NameAndType #17:#18 // "<init>":()V
- 第三十五个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 07 ———— UTF-8 编码的字符串长度:7
57 65 6C 63 6F 6D 65 ———— Welcome
#35 = Utf8 Welcome
- 第三十六个常量
# 结构
CONSTANT_NameAndType_info {
u1 tag
u2 index // 指向该字段或方法名称常量项的索引
u2 index // 指向该字段或方法描述符常量项的索引
}
0C ———— CONSTANT_NameAndType_info
00 0B ———— 该字段或方法名称常量项的索引:11
00 0C ———— 该字段或方法描述符常量项的索引:12
#36 = NameAndType #11:#12 // str:Ljava/lang/String;
- 第三十七个常量
# 结构
CONSTANT_NameAndType_info {
u1 tag
u2 index // 指向该字段或方法名称常量项的索引
u2 index // 指向该字段或方法描述符常量项的索引
}
0C ———— CONSTANT_NameAndType_info
00 0D ———— 该字段或方法名称常量项的索引:13
00 0E ———— 该字段或方法描述符常量项的索引:14
#37 = NameAndType #13:#14 // x:I
- 第三十八个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 3C ———— UTF-8 编码的字符串长度:60
63 6F 6D 2F 62 61 79 65 72 6E 2F 73 68 65 6E 67 73 69 79 75 61 6E 2F 6A 76 6D 5F 6C 65 63 74 75 72 65 2F 62 79 74 65 63 6F 64 65 2F 6C 65 73 73 6F 6E 31 33 2F 4D 79 54 65 73 74 32 ———— com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2
#38 = Utf8 com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2
- 第三十九个常量
# 结构
CONSTANT_NameAndType_info {
u1 tag
u2 index // 指向该字段或方法名称常量项的索引
u2 index // 指向该字段或方法描述符常量项的索引
}
0C ———— CONSTANT_NameAndType_info
00 1D ———— 该字段或方法名称常量项的索引:29
00 1E ———— 该字段或方法描述符常量项的索引:30
#39 = NameAndType #29:#30 // setX:(I)V
- 第四十个常量
# 结构
CONSTANT_Class_info {
u1 tag
u2 index // 指向全限定名常量项的索引
}
07 ———— CONSTANT_Class_info
00 2C ———— 全限定名常量项的索引:44
#40 = Class #44 // java/lang/Integer
- 第四十一个常量
# 结构
CONSTANT_NameAndType_info {
u1 tag
u2 index // 指向该字段或方法名称常量项的索引
u2 index // 指向该字段或方法描述符常量项的索引
}
0C ———— CONSTANT_NameAndType_info
00 2D ———— 该字段或方法名称常量项的索引:45
00 2E ———— 该字段或方法描述符常量项的索引:46
#41 = NameAndType #45:#46 // valueOf:(I)Ljava/lang/Integer;
- 第四十二个常量
# 结构
CONSTANT_NameAndType_info {
u1 tag
u2 index // 指向该字段或方法名称常量项的索引
u2 index // 指向该字段或方法描述符常量项的索引
}
0C ———— CONSTANT_NameAndType_info
00 0F ———— 该字段或方法名称常量项的索引:15
00 10 ———— 该字段或方法描述符常量项的索引:16
#42 = NameAndType #15:#16 // in:Ljava/lang/Integer;
- 第四十三个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 10 ———— UTF-8 编码的字符串长度:16
6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 ———— java/lang/Object
#43 = Utf8 java/lang/Object
- 第四十四个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 11 ———— UTF-8 编码的字符串长度:17
6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 ———— java/lang/Integer
#44 = Utf8 java/lang/Integer
- 第四十五个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 07 ———— UTF-8 编码的字符串长度:7
76 61 6C 75 65 4F 66 ———— valueOf
#45 = Utf8 valueOf
- 第四十六个常量
# 结构
CONSTANT_Utf8_info {
u1 tag
u2 length // UTF-8 编码的字符串长度
u1 bytes // 长度为 length 的 UTF-8 编码的字符串
}
01 ———— CONSTANT_Utf8_info
00 16 ———— UTF-8 编码的字符串长度:22
28 49 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B ———— (I)Ljava/lang/Integer;
#46 = Utf8 (I)Ljava/lang/Integer;
四,access_flags ———— 类的访问控制权限
格式:
u2 access_flags;
内容:
00 21 // ACC_SUPER 与 ACC_PUBLIC 的并集
五,this_class ———— 类名
格式:
u2 this_class
内容:
00 05 // 常量池中索引为 5 的常量项:CONSTANT_Class_info,最终指向 「 com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2 」
六,super_class ———— 父类名
格式:
u2 super_class
内容:
00 0A // 常量池中索引为 10 的常量项:CONSTANT_Class_info,最终指向 「 java/lang/Object 」
七,接口集合
7.1 interfaces_count ———— 接口个数
u2 interface_count
内容:
00 00 // 接口个数为 0
八,字段集合
8.1 fields_count ———— 字段个数
格式:
u2 fields_count
内容:
00 03 // 字段个数为 3
8.2 field_info ———— 字段表
格式:
field_info fields[fields_count] // 字段表的个数为 3
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
内容:
- 第一个字段表 ———— 「String str」
00 00 ———— access_flags:-
00 0B ———— name_index :指向常量池中索引为11的常量,对应常量 「 str 」
00 0C ———— descriptor_index :指向常量池中索引为12的常量,对应常量 「 Ljava/lang/String; 」
00 00 ———— attributes_count :0
- 第二个字段表 ———— 「private int x」
00 02 ———— access_flags:private
00 0D ———— name_index :指向常量池中索引为13的常量,对应常量 「 x 」
00 0E ———— descriptor_index :指向常量池中索引为14的常量,对应常量 「 I 」
00 00 ———— attributes_count :0
- 第三个字段表 ———— 「public static Integer in」
00 09 ———— access_flags:public static
00 0F ———— name_index :指向常量池中索引为15的常量,对应常量 「 in 」
00 10 ———— descriptor_index :指向常量池中索引为16的常量,对应常量 「 Ljava/lang/Integer; 」
00 00 ———— attributes_count :0
九,方法集合
9.1 methods_count ———— 方法的个数
格式:
u2 methods_count
内容:
00 04 // 方法个数为 4
9.2 method_info ———— 方法表
method_info 数据格式:
method_info methods[methods_count] // 方法表的个数为 4
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
Code 属性数据结构:
Code attribute 的作用是保存该方法的结构,如所对应的字节码
Code_attribute {
u2 attribute_name_index; // 属性名称索引
u4 attribute_length; // 属性长度。不包括’attribute_length’、’attribute_name_index’。也就继续往下 length 个字节。
u2 max_stack; // 该方法运行的任何时刻所能达到的操作数栈的最大深度
u2 max_locals; // 方法执行期间可以创建的最大局部变量个数
u4 code_length; // 表示该方法所包含的字节码的字节数
u1 code[code_length]; // 具体字节码。即,该方法被调用时,虚拟机所执行的字节码
u2 exception_table_length; // 异常表的长度
{ u2 start_pc; // start_pc 和 end_pc 表示 code 数组中从 [start_pc, end_pc) 处的指令抛出的异常会由这个表项来处理
u2 end_pc;
u2 handler_pc; // 表示处理异常的代码的开始处。
u2 catch_type; // 表示会被处理的异常类型,它指向常量池里的一个异常类。当 catch_type 为 0 时,表示处理所有的异常
} exception_table[exception_table_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
LineNumberTable(行号表):
这个属性用来表示 code 数组中的字节码和 Java 代码行数之间的关系。这个属性可以用来在调试的时候定位代码执行的行数
LineNumberTable_attribute {
u2 attribute_name_index; // 属性名称索引
u4 attribute_length; // 属性长度。不包括’attribute_length’、’attribute_name_index’。也就继续往下 length 个字节。
u2 line_number_table_length;
{ u2 start_pc; // 对于某源代码指令,start_pc:对应该指令对应的字节码,在其字节码数组(code[code_length])中的索引;line_number:对应该执行在源码文件中的行数
u2 line_number;
} line_number_table[line_number_table_length];
}
LocalVariableTable(局部变量表):
LocalVariableTable_attribute {
u2 attribute_name_index; // 属性名称索引
u4 attribute_length; // 属性长度。不包括’attribute_length’、’attribute_name_index’。也就继续往下 length 个字节。
u2 local_variable_table_length; // 表示’局部变量的个数’
{ u2 start_pc; // 当给定的变量处在 code 数组的 [start_pc, start_pc + length)范围内时,该局部变量必定为某个值
u2 length;
u2 name_index; // 局部变量名字对应在常量池中的索引
u2 descriptor_index; // 局部变量描述符对应在常量池中的索引
u2 index; // 表示 index,当前这个局部变量在局部变量表数组中的索引值
} local_variable_table[local_variable_table_length];
}
内容:
- 第一个方法表 ———— 构造方法
因为我们没有显示的声明类的构造方法,因此这里是由 JVM 帮我们生产的默认的构造方法
00 01 ———— access_flags:public
00 11 ———— name_index:指向常量池中索引为17的常量,对应常量「 <init> 」
00 12 ———— descriptor_index:指向常量池中索引为18的常量;对应常量「 ()V 」
00 01 ———— attributes_count:1
00 13 ———— attribute_name_index:指向常量池中索引为19的常量,对应常量「 Code 」
00 00 00 42 ———— attribute_length:66
00 02 ———— max_stack :操作数栈的最大深度为 2
00 01 ———— max_locals :局部变量的最大个数为 1
00 00 00 10 ———— code_length :16
2A ———— aload_0;将局部变量表中索引为 0 的引用类型变量(即,this 变量)压入操作数栈中;
B7 00 01 ———— invokespecial #1 <java/lang/Object.<init>>;执行当前类的父类的构造函数,对父类进行初始化
2A ———— aload_0;将局部变量表中索引为 0 的引用类型变量(即,this 变量)压入操作数栈中;(之前压入操作数栈的 this 变量已经在执行 invokespecial 的时候弹出栈作为 invokespecial 执行的参数使用了)
12 02 ———— ldc #2 <Welcome> ;将常量池中的「Welcome」字符串压入操作数栈顶
B5 00 03 ———— putfield #3 <com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.str>;设置「String str」字段的值为「Welcome」
2A ———— aload_0;将局部变量表中索引为 0 的引用类型变量(即,this 变量)压入操作数栈中
08 ———— iconst_5;将整数常量值 5 压入操作数栈顶
B5 00 04 ———— putfield #4 <com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.x>;;设置「private int x」字段的值为「5」
B1 ———— return;方法结束返回
00 00 ———— exception_table_length :0
00 02 ———— attributes_count :2
00 14 ———— attribute_name_index:指向常量池中索引为20的常量,对应常量「 LineNumberTable 」
00 00 00 0E ———— attribute_length:14
00 03 ———— line_number_table_length :3
00 00 00 03 ———— start_pc:0;line_number:3;从 code[0]开始的字节码指令(即,2A)对应于 Java 源代码是行号 3 的 Java 代码
00 04 00 04 ———— start_pc:4;line_number:4
00 0A 00 06 ———— start_pc:10;line_number:6
00 15 ———— attribute_name_index:指向常量池中索引为21的常量,对应常量「 LocalVariableTable 」
00 00 00 0C ———— attribute_length:12
00 01 ———— local_variable_table_length :1
00 00 ———— start_pc :0
00 10 ———— length :16 ;指这个局部变量的处在 code 数组的[start_pc, start_pc + lenght) 索引范围内。
00 16 ———— name_index:指向常量池中索引为22的常量,对应常量「 this 」
00 17 ———— descriptor_index:指向常量池中索引为23的常量,对应常量「 Lcom/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2; 」
00 00 ———— index:0
👆再次说明,类的成员变量是在构造方法中实现真正的赋值的!
- 第二个方法 ———— 「public static void main(String[] args) 」
00 09 ———— access_flags:public static
00 18 ———— name_index:指向常量池中索引为24的常量;对应常量「 main 」
00 19 ———— descriptor_index:指向常量池中索引为25的常量,对应常量「 ([Ljava/lang/String;)V 」
00 01 ———— attributes_count:1
00 13 ———— attribute_name_index:指向常量池中索引为19的常量,对应常量「 Code 」
00 00 00 57 ———— attribute_length:87
00 02 ———— max_stack :操作数栈的最大深度为 2
00 02 ———— max_locals :局部变量的最大个数为 2
00 00 00 17 ———— code_length :23
BB 00 05 ———— new #5 <com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2>;创建一个 MyTest2 对象(分配了内存,并将实例变量进行了默认初始化,即,实例变量的值都为默认值,而非我们定义的真是值),并将对象引用压入栈顶
59 ———— dup;拷贝栈顶元素,将拷贝的元素压入栈顶
B7 00 06 ———— invokespecial #6 <com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.<init>>;执行 MyTest2 类的构造方法。
4C ———— astore_1;将栈顶元素弹出(即,myTest2 对象引用),放入局部变量表索引为 1 的 slot 中。
2B ———— aload_1;将局部变量表中索引为 1 的变量(即,myTest2 对象引用)压入操作数栈中;
10 08 ———— bipush 8;将数值 8 压入操作数栈顶
B6 00 07 ———— invokevirtual #7 <com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.setX>;执行「myTest2.setX(8);」方法
10 14 ———— bipush 20;将数值 20 压入操作数栈顶
B8 00 08 ———— invokestatic #8 <java/lang/Integer.valueOf>;执行自动装箱操作,将数值 20 转化为 Integer 对象,并将这个 Integer 对象压入操作数栈
B3 00 09 ———— putstatic #9 <com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.in>;将「20封装的 Integer 对象(通过弹出操作数栈栈顶元素得到)」赋值给静态变量「public static Integer in」
B1 —— return;方法结束返回
00 00 ———— exception_table_length :0
00 02 ———— attributes_count :2
00 14 ———— attribute_name_index:指向常量池中索引为20的常量,对应常量「 LineNumberTable 」
00 00 00 12 ———— attribute_length:18
00 04 ———— line_number_table_length :4
00 00 00 0B ———— start_pc:0;line_number:11
00 08 00 0D ———— start_pc:8;line_number:13
00 0E 00 0F ———— start_pc:14;line_number:15
00 16 00 10 ———— start_pc:22;line_number:16
00 15 ———— attribute_name_index:指向常量池中索引为21的常量,对应常量「 LocalVariableTable 」
00 00 00 16 ———— attribute_length:22
00 02 ———— local_variable_table_length :2
00 00 ———— start_pc :0
00 17 ———— length :23
00 1A ———— name_index:指向常量池中索引为26的常量,对应常量「 args 」
00 1B ———— descriptor_index:对应常量「 [Ljava/lang/String; 」
00 00 ———— index:0(在 LocalVariableTable 中的索引)
00 08 ———— start_pc :8
00 0F ———— length :15
00 1C ———— name_index:指向常量池中索引为28的常量,对应常量「 myTest2 」
00 17 ———— descriptor_index:对应常量「 Lcom/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2; 」
00 01 ———— index:1(在 LocalVariableTable 中的索引)
「MyTest2 myTest2 = new MyTest2();」操作实际上完成了 3 件事情:
① 为这个对象在内存,也就是在堆上开辟一个内存空间
BB 00 05 ———— new #5 <com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2>;创建一个 MyTest2 对象(分配了内存,并将实例变量进行了默认初始化,即,实例变量的值都为默认值,而非我们定义的真是值),并将对象引用压入栈顶
59 ———— dup;拷贝栈顶元素,将拷贝的元素压入栈顶;用于下一步「invokespecial」指令的第二个参数传入。
② 执行构造方法
B7 00 06 ———— invokespecial #6 <com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.<init>>;执行 MyTest2 类的构造方法。「invokespecial」指令的第一个参数是符号引用‘<com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.<init>>’,第二个参数是,操作数栈顶的元素(👈注意,这里涉及到弹栈操作。所以才有了👆一步的将操作数栈顶的元素拷贝一份,并且压入操作数栈中)。
③ 构造方法执行完后,将其在堆上所生成的该对象的引用值返回
4C ———— astore_1;将栈顶元素弹出(即,myTest2 对象引用),放入局部变量表索引为 1 的 slot 中。
- 第三个方法 ———— 「public void setX(int x)」
00 01 ———— access_flags:public
00 1D ———— name_index:指向常量池中索引为29的常量,对应常量「 setX 」
00 1E ———— descriptor_index:指向常量池中索引为30的常量,对应常量「 (I)V 」
00 01 ———— attributes_count:1
00 13 ———— attribute_name_index:指向常量池中索引为19的常量,对应常量「 Code 」
00 00 00 3E ———— attribute_length:62
00 02 ———— max_stack :操作数栈的最大深度为 2
00 02 ———— max_locals :局部变量的最大个数为 2
00 00 00 06 ———— code_length :6
2A ———— aload_0;将局部变量表中索引为 0 的引用类型变量(即,this 变量)压入操作数栈中;
1B ———— iload_1;将局部变量表中索引为 1 的 int 类型变量(即,this 变量)压入操作数栈中;
B5 00 04 ———— putfield #4 <com/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2.x>;设置 this 的成员变量 x 的值为方法传入参数 x。因为 JVM 的字节码是基于操作数栈执行的。因此,这里的 this 对象和方法参数 x 都是通过操作数栈得到的,所以我们总是先将数据压入栈中,在指令执行的时候会通过弹栈来获取所需的数据。
B1 ———— return;方法结束返回
00 00 ———— exception_table_length :0
00 02 ———— attributes_count :2
00 14 ———— attribute_name_index:指向常量池中索引为20的常量,对应常量「 LineNumberTable 」
00 00 00 0A ———— attribute_length:10
00 02 ———— line_number_table_length :2
00 00 00 13 ———— start_pc:0;line_number:19
00 05 00 14 ———— start_pc:5;line_number:20
00 15 ———— attribute_name_index:指向常量池中索引为21的常量,对应常量「 LocalVariableTable 」
00 00 00 16 ———— attribute_length:22
00 02 ———— local_variable_table_length :2
00 00 ———— start_pc :0
00 06 ———— length :6
00 16 ———— name_index:指向常量池中索引为22的常量,对应常量「 this 」
00 17 ———— descriptor_index:指向常量池中索引为23的常量,对应常量「 Lcom/bayern/shengsiyuan/jvm_lecture/bytecode/lesson13/MyTest2; 」
00 00 ———— index:0(在 LocalVariableTable 中的索引)
00 00 ———— start_pc :0
00 06 ———— length :6
00 0D ———— name_index:指向常量池中索引为13的常量,对应常量「 x 」
00 0E ———— descriptor_index:指向常量池中索引为14的常量,对应常量「 I 」
00 01 ———— index:1(在 LocalVariableTable 中的索引)
注意,「public void setX(int x)」方法的「args_size=2」。
对于 Java 类中的每一个实例方法(非 static 方法),其在编译后所生产的字节码当中,方法参数的数量总是会比源代码中方法参数的数量多一个(this),它位于方法的第一个参数位置处;这样,我们就可以在 Java 的实例方法中使用 this 来去访问当前对象的属性以及其他方法。
这个操作是在编译期间完成的,即,由 javac 编译器在编译的时候将对 this 的访问转化为对一个普通实例方法参数的访问;接下来在运行期间,由 JVM 在调用实例方法时,自动向实例方法传入该 this 参数。所以,在实例方法的局部变量表中,至少会有一个指向当前对象的局部变量。
- 第四个方法 ———— 类初始化方法
00 08 ———— access_flags:static
00 1F ———— name_index:指向常量池中索引为31的常量,对应常量「 <clinit> 」
00 12 ———— descriptor_index:指向常量池中索引为18的常量,对应常量「 ()V 」
00 01 ———— attributes_count:1
00 13 ———— attribute_name_index:指向常量池中索引为19的常量,对应常量「 Code 」
00 00 00 21 ———— attribute_length:33
00 01 ———— max_stack :操作数栈的最大深度为 1
00 00 ———— max_locals :局部变量的最大个数为 0
00 00 00 09 ———— code_length :9
10 0A ———— bipush 10;将数值 10 压入操作数栈顶
B8 00 08 ———— invokestatic #14 <java/lang/Integer.valueOf>;;执行自动装箱操作,将数值 10 转化为 Integer 对象,并将这个 Integer 对象压入操作数栈
B3 00 09 ———— putstatic #15 <com/bayern/shengsiyuan/jvm_lecture/bytecode/MyTest2.in>;将「10封装的 Integer 对象(通过弹出操作数栈栈顶元素得到)」赋值给类的静态变量「public static Integer in」
B1 ———— return;方法结束返回
00 00 ———— exception_table_length :0
00 01 ———— attributes_count :1
00 14 ———— attribute_name_index:指向常量池中索引为20的常量,对应常量「 LineNumberTable 」
00 00 00 06 ———— attribute_length:6
00 01 ———— line_number_table_length :1
00 00 00 08 ———— start_pc:0;line_number:8
👆静态变量的赋值就在 clinit 中
十,属性集合
10.1 attributes_count ———— 附加属性的个数
格式:
u2 attributes_count;
内容:
00 01 // 附加属性的个数为 1
10.2 attributes ———— 附加属性表
格式:
attribute_info attributes[attributes_count];
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
}
内容:
00 20 ———— attribute_name_index:对应常量「 SourceFile 」
00 00 00 02 ———— attribute_length:2
00 21 ———— sourcefile_index:对应常量「 MyTest2.java 」