在Java字节码文件结构剖析(一)中我们己经详细分析了简单的类 Mytest35的字节码,今天,我们再来分析一个复杂一点的类。
如下:
MyTest35_1.java
package com.spring_1_100.test_31_40.test35_resource_inject; public class MyTest35_1 { String str = "Welcome"; private int x = 5; private static Integer in = 10; public static void main(String[] args) { MyTest35_1 myTest35_1 = new MyTest35_1(); myTest35_1.setX(8); in = 20; } public void setX(int x) { this.x = x; } }
运行 java -verbose ,得到 Classfile
Classfile /Users/quyixiao/project/spring_tiny/target/classes/com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.class Last modified 2020-10-10; size 911 bytes MD5 checksum 4a1f4eae2b03a3a38505911fd4ffa3e4 Compiled from "MyTest35_1.java" public class com.spring_1_100.test_31_40.test35_resource_inject.MyTest35_1 minor version: 0 major version: 51 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #10.#34 // java/lang/Object."":()V #2 = String #35 // Welcome #3 = Fieldref #5.#36 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.str:Ljava/lang/String; #4 = Fieldref #5.#37 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.x:I #5 = Class #38 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1 #6 = Methodref #5.#34 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1."":()V #7 = Methodref #5.#39 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.setX:(I)V #8 = Methodref #40.#41 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer; #9 = Fieldref #5.#42 // com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.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/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1; #24 = Utf8 main #25 = Utf8 ([Ljava/lang/String;)V #26 = Utf8 args #27 = Utf8 [Ljava/lang/String; #28 = Utf8 myTest35_1 #29 = Utf8 setX #30 = Utf8 (I)V #31 = Utf8 <clinit> #32 = Utf8 SourceFile #33 = Utf8 MyTest35_1.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/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1 #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: public com.spring_1_100.test_31_40.test35_resource_inject.MyTest35_1(); descriptor: ()V flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()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 5: 10 LocalVariableTable: Start Length Slot Name Signature 0 16 0 this Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1; 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/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1 3: dup 4: invokespecial #6 // Method "":()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; 19: putstatic #9 // Field in:Ljava/lang/Integer; 22: return LineNumberTable: line 9: 0 line 10: 8 line 11: 14 line 12: 22 LocalVariableTable: Start Length Slot Name Signature 0 23 0 args [Ljava/lang/String; 8 15 1 myTest35_1 Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1; 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 15: 0 line 16: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1; 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 6: 0 } SourceFile: "MyTest35_1.java"
得到十六进制字节码:
CA FE BA BE 00 00 00 33 00 2F 0A 00 0A 00 22 08 00 23 09 00 05 00 24 09 00 05 00 25 07 00 26 0A 00 05 00 22 0A 00 05 00 27 0A 00 28 00 29 09 00 05 00 2A 07 00 2B 01 00 03 73 74 72 01 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 01 00 01 78 01 00 01 49 01 00 02 69 6E 01 00 13 4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B 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 3F 4C 63 6F 6D 2F 73 70 72 69 6E 67 5F 31 5F 31 30 30 2F 74 65 73 74 5F 33 31 5F 34 30 2F 74 65 73 74 33 35 5F 72 65 73 6F 75 72 63 65 5F 69 6E 6A 65 63 74 2F 4D 79 54 65 73 74 33 35 5F 31 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 6D 79 54 65 73 74 33 35 5F 31 01 00 04 73 65 74 58 01 00 04 28 49 29 56 01 00 08 3C 63 6C 69 6E 69 74 3E 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00 0F 4D 79 54 65 73 74 33 35 5F 31 2E 6A 61 76 61 0C 00 11 00 12 01 00 07 57 65 6C 63 6F 6D 65 0C 00 0B 00 0C 0C 00 0D 00 0E 01 00 3D 63 6F 6D 2F 73 70 72 69 6E 67 5F 31 5F 31 30 30 2F 74 65 73 74 5F 33 31 5F 34 30 2F 74 65 73 74 33 35 5F 72 65 73 6F 75 72 63 65 5F 69 6E 6A 65 63 74 2F 4D 79 54 65 73 74 33 35 5F 31 0C 00 1D 00 1E 07 00 2C 0C 00 2D 00 2E 0C 00 0F 00 10 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 00 11 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 01 00 07 76 61 6C 75 65 4F 66 01 00 16 28 49 29 4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B 00 21 00 05 00 0A 00 00 00 03 00 00 00 0B 00 0C 00 00 00 02 00 0D 00 0E 00 00 00 0A 00 0F 00 10 00 00 00 04 00 01 00 11 00 12 00 01 00 13 00 00 00 42 00 02 00 01 00 00 00 10 2A B7 00 01 2A 12 02 B5 00 03 2A 08 B5 00 04 B1 00 00 00 02 00 14 00 00 00 0E 00 03 00 00 00 03 00 04 00 04 00 0A 00 05 00 15 00 00 00 0C 00 01 00 00 00 10 00 16 00 17 00 00 00 09 00 18 00 19 00 01 00 13 00 00 00 57 00 02 00 02 00 00 00 17 BB 00 05 59 B7 00 06 4C 2B 10 08 B6 00 07 10 14 B8 00 08 B3 00 09 B1 00 00 00 02 00 14 00 00 00 12 00 04 00 00 00 09 00 08 00 0A 00 0E 00 0B 00 16 00 0C 00 15 00 00 00 16 00 02 00 00 00 17 00 1A 00 1B 00 00 00 08 00 0F 00 1C 00 17 00 01 00 01 00 1D 00 1E 00 01 00 13 00 00 00 3E 00 02 00 02 00 00 00 06 2A 1B B5 00 04 B1 00 00 00 02 00 14 00 00 00 0A 00 02 00 00 00 0F 00 05 00 10 00 15 00 00 00 16 00 02 00 00 00 06 00 16 00 17 00 00 00 00 00 06 00 0D 00 0E 00 01 00 08 00 1F 00 12 00 01 00 13 00 00 00 21 00 01 00 00 00 00 00 09 10 0A B8 00 08 B3 00 09 B1 00 00 00 01 00 14 00 00 00 06 00 01 00 00 00 06 00 01 00 20 00 00 00 02 00 21
关于字节码的基本知识,己经在 Java字节码文件结构剖析(一)中己经有了详细的解析,在这里,我就不做过多的缀述了。直接分析字节码了。
先来了解一下字节码的完全结构 :
类字节码文件完整结构 :
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];
}
Class 文件结构中常量池中11种数据类型的结构总表
Class 文件结构中常量池中11种数据类型的结构总表 | |||
---|---|---|---|
常量 | 项目 | 类型 | 描述 |
CONSTANT_Utf8_info | tag | U1 | 值为1 |
length | U2 | UTF-8编码字符串长度 | |
bytes | U(length) | 长度为 length 的 UTF-8编码的字符串 | |
CONSTANT_Integer_info | tag | U1 | 值为3 |
bytes | U4 | 按照高位在前存储 int 值 | |
CONSTANT_Float_info | tag | U1 | 值为4 |
bytes | U4 | 按照高位在前存储 float 值 | |
CONSTANT_Long_info | tag | U1 | 值为5 |
bytes | U8 | 按照高位在前存储 long 值 | |
CONSTANT_Double_info | tag | U1 | 值为6 |
bytes | U8 | 按照高位在前存储 double 值 | |
CONSTANT_Class_info | tag | U1 | 值为7 |
index | U2 | 指向全限定名常量项的索引 | |
CONSTANT_String_info | tag | U1 | 值为8 |
index | U2 | 指向字符串字面量索引 | |
CONSTANT_FieIdref_info | tag | U1 | 值为9 |
index | U2 | 指向声明字段的类或者接口描述符CONSTANT_Class_info的索引项 | |
index | U2 | 指向字段描述符 CONSTANT_NameAndType_Info 的索引项 | |
CONSTANT_Methodref_info | tag | U1 | 值为10 |
index | U2 | 指向声明方法的类描述符CONSTANT_Class_info的索引项 | |
index | U2 | 指向名称及类型描述符 CONSTANT_NameAndType_Info 的索引项 | |
CONSTANT_InterfaceMethodref_info | tag | U1 | 值为11 |
index | U2 | 指向声明方法的类描述符CONSTANT_Class_info的索引项 | |
index | U2 | 指向名称及类型描述符 CONSTANT_NameAndType_Info 的索引项 | |
CONSTANT_NameAndType_info | tag | U1 | 值为12 |
index | U2 | 指向该字段或方法名称常量项索引 | |
index | U2 | 指向该字段或方法描述符常量项的索引 |
-
CA FE BA BE:魔数,每个 class 字节码都是这样定义的
-
00 00 00 33:次版本号(minor version: 0) ,主版本号 (major version: 51)
-
00 2F :常量池的个数,47个,常量池数组中元素的个数=常量池数-1(其中0暂时不使用)。目的是满足某些常量池索引值的数据在特定情况下需要表达【不引用任何一个常量池】 的含义,根本原因在于,索引为0也是一个常量(保留常量),只不过它不位于常量表中,这个常量就对应 null 值,所以常量池的索引 从1而非0开始。
-
#1 0A 00 0A 00 22:0A 10进制数是10,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Methodref_info,前两个字节#10,后两个字节#34, 而#34分别指向#17,#18,一起组合起来就是java/lang/Object,"&7lt;init>": ()V,父类的 Object ,返回值为void 类型的构造方法。
-
#2 08 00 23 :08 10进制数是8,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_String_info,指向字符串字面量索引占两个字节,00 23 的十进制数为35,指向常量池中的#35,08 00 23 表示字符串Welcome。
-
#3 09 00 05 00 24 :09的10进制数是9,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_FieIdref_info,分别指向了#5,#36,整体组合起来的意思就是MyTest35_1类中的 String 对象(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.str:Ljava/lang/String; )
-
#4 09 00 05 00 25 :略,分别指向了#5,#37 ,代表着MyTest35_1对象中定义了一个 Int 类型的属性 x (com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.x:I)
-
#5 07 00 26 : 07 的10进制数是9,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Class_info,后面两个字节表示指向全限定名常量项的索引,因此指向常量池中#38,表示com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1类
-
#6 0A 00 05 00 22 :略,后4个字节分别指向了常量池中#5,#34,表示 MyTest35_1的构造方法(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1."": ()V)
-
#7 0A 00 05 00 27 :略,后4个字节分别指向了常量池中#5,#39,表示MyTest35_1类中返回值为 void类型的 setX 方法(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.setX:(I)V)
-
#8 0A 00 28 00 29 :略:后4个字节分别指向了常量池中#40,#41,java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
-
#9 09 00 05 00 2A :略,分别指向了#5,#42,表示MyTest35_1对象中有一个变量 in,为 Integer 类型(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.in:Ljava/lang/Integer;)
-
#10 07 00 2B:07的10进制数是7,在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Class_info,分别指向了#43,表示java/lang/Object对象。
-
#11 01 00 03 73 74 72 :01在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Utf8_info,后两个字节00 03表示字符串编码所占长度为3,因此向生数3个字节,表示 str
-
#12 01 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B :略,后面18个字节表示,Ljava/lang/String;
-
#13 01 00 01 78 :略,后面一个字节为 x
-
#14 01 00 01 49 : 略 ,后面一个字节 为I
-
#15 01 00 02 69 6E :略,后面两个字节为 in
-
#16 01 00 13 4C 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B : 后面19个字节为Ljava/lang/Integer;
-
#17 01 00 06 3C 69 6E 69 74 3E :略,后面6个字节为<init>
-
#18 01 00 03 28 29 56 :略,()V
-
#19 01 00 04 43 6F 64 65 :略,Code
-
#20 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 :略,LineNumberTable
-
#21 01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 :略,LocalVariableTable
-
#22 01 00 04 74 68 69 73 :略,this
-
#23 01 00 3F 4C 63 6F 6D 2F 73 70 72 69 6E 67 5F 31 5F 31 30 30 2F 74 65 73 74 5F 33 31 5F 34 30 2F 74 65 73 74 33 35 5F 72 65 73 6F 75 72 63 65 5F 69 6E 6A 65 63 74 2F 4D 79 54 65 73 74 33 35 5F 31 3B :略,Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;
-
#24 01 00 04 6D 61 69 6E :略,main
-
#25 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 :略,([Ljava/lang/String;)V
-
#26 01 00 04 61 72 67 73 :args
-
#27 01 00 13 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B :略,[Ljava/lang/String;
-
#28 01 00 0A 6D 79 54 65 73 74 33 35 5F 31 :略,myTest35_1
-
#29 01 00 04 73 65 74 58 :略,setX
-
#30 01 00 04 28 49 29 56 : 略,(I)V
-
#31 01 00 08 3C 63 6C 69 6E 69 74 3E :略,<clinit>
-
#32 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 :略,SourceFile
-
#33 01 00 0F 4D 79 54 65 73 74 33 35 5F 31 2E 6A 61 76 61:略,MyTest35_1.java
-
#34 0C 00 11 00 12 : 0C在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_NameAndType_info,分别指向了 #17:#18,表示一个 void 类型的构造函数("": ()V)
-
#35 01 00 07 57 65 6C 63 6F 6D 65 :略,Welcome
-
#36 0C 00 0B 00 0C :分别指向了 #11:#12,表示 定义了一个String 类型的 str变量(str:Ljava/lang/String;)
-
#37 0C 00 0D 00 0E :分别指向了 #13:#14,表示一个int 类型的变量 x(x:I)
-
#38 01 00 3D 63 6F 6D 2F 73 70 72 69 6E 67 5F 31 5F 31 30 30 2F 74 65 73 74 5F 33 31 5F 34 30 2F 74 65 73 74 33 35 5F 72 65 73 6F 75 72 63 65 5F 69 6E 6A 65 63 74 2F 4D 79 54 65 73 74 33 35 5F 31 : 略,表示常量 com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1
-
#39 0C 00 1D 00 1E :分别指向了#29:#30 ,表示定义了一个返回值为 void 类型,传入参数为 int 类型的方法 setX (setX:(I)V)
-
#40 07 00 2C :07在【Class 文件结构中常量池中11种数据类型的结构总表】中对应的是CONSTANT_Class_info,后面占两个字节,指向#44 ,表示一个 Integer类型( java/lang/Integer),这里再次提一下,为什么我知道07对应的是CONSTANT_Class_info,是根据Class 文件结构中常量池中11种数据类型的结构总表来的,而为什么后面取两个字节,也是根据总表来的。
-
#41 0C 00 2D 00 2E : 分别指向常量池中#45:#46,表示Integer对象中的 valueOf()方法传入一个 int类型的值(valueOf:(I)Ljava/lang/Integer;)
-
#42 0C 00 0F 00 10 :分别指向了常量池中#15:#16,表示in 是 Integer 类型对象(in:Ljava/lang/Integer;)
-
#43 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 :略,java/lang/Object
-
#44 01 00 11 6A 61 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 :略,java/lang/Integer
-
#45 01 00 07 76 61 6C 75 65 4F 66 :略,valueOf
-
#46 01 00 16 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; 到这里常量池中己经解析完成。
-
00 21 (access_flags):根据类字节码文件完整结构 ,我们得到常量池后面是占两个字节的 access_flags,0021 表示的是ACC_PUBLIC(0x0001)和ACC_SUPER(0x0020)的组合。
-
00 05 (this_class):当前类占两个字节指向常量池中#5,当前类的全额限定名(com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1)
-
00 0A (super_class): 当前类后面占两个字节为当前类的父类,指向#10,为java/lang/Object
-
00 00(interfaces_count):紧接着是当前类的接口数,当前类的接口数为0
-
00 03 (fields_count):当前类的属性个数3
属性表结构
field_info{
u2 access_flags; 0002
u2 name_index; 0006
u2 descriptor_index; 0006
u2 attributes_count; 0000
attribute_info attributes[attributes_count];
}
- String str = “Welcome”;
- 00 00 (access_flags):0000 表示没有访问修饰符
- 00 0B(name_index):指向常量池中的#11,表示字符串 str
- 00 0C (descriptor_index):指向常量池中的#12,表示Ljava/lang/String;一个String 类型的对象
- 00 00 (attributes_count):属性个数为0
- private int x = 5;
- 00 02 (access_flags):0002 表示 private 类型
- 00 0D (name_index):属性名指向常量池中#13,表示x
- 00 0E (descriptor_index):属性类型,指向常量池中#14,表示 int 类型
- 00 00 (attributes_count):属性个数为0
- private static Integer in = 10;
- 00 0A (access_flags):我们知道 private 类型的对应的是0x0002,static 的对应的是0x0008,因此,两者相加的16进制数为0x000A,具体参照 Java字节码文件结构剖析(一) 的 类的访问标识符和16进制对应关系
- 00 0F(name_index):变量名指向常量池中#15,为 in
- 00 10 (descriptor_index):变量类型指向常量池中#16,为Ljava/lang/Integer;
- 00 00 (attributes_count):属性个数
- 00 04(methods_count) :方法个数为4
在解析之前,我们来了解一下方法的结构相关的基本信息
1.方法表结构
method_info{
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
2.方法的属性结构,方法中每个属性都是一个 attribute_info 结构 ,JVM预定义了部分 attribute,但是编译器自己也可以实现了自己的 attribute 写入 class 文件里,供运行时使用。不同的 attribute 通过attribute_name_index 来区分。
attribute_info{
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
3.Code结构:Code Attribute 的作用是保存该方法的结构,如所对应的字节码
Code_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_lenght];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
- attribute_length:表示 attribute所包含的字节数,不包含 attribute_name_index 和attribute_length 字段 。
- max_stack:表示这个方法运行的任何时刻所能达到的操作数栈的最大深度。
- max_locals:表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量。
- code_length:表示该方法所包含的字节码的字节数以及具体的指令码。具体字节码即是该方法被调用时,虚拟机所执行的字节码。
- exception_table:这里存放的是处理异常的信息。每个exception_table 表项由start_pc,end_pc,handler_pc,catch_type 组成
- start_pc 和 end_pc :表示在 code数组中的从 start_pc 到 end_pc (包含 start_pc,不包含 end_pc) 的指令抛出的异常会由这个表项来处理。
- handler_pc:表示处理异常的代码的开始处。 catch_type:表示会被处理的异常类型,它指向常量池里的一个异常类,当 catch_type 为0时,表示处理所有的异常。
附加属性
LineNumberTable:这个属性用来表示 code 数组中的字节码和 Java 代码行数之间的关系,这个属性可以用来在调试的时候定位代码执行的行数。
LineNumberTable 的结构录下:
LineNumberTable_attribute{
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{
u2 start_pc;
u2 line_number;
}
}
助记符
- aload_0 = 42 (0x2a) : 将索引为0的元素推送到操作栈的栈顶。
- invokespecial :调用实例方法,后面可以接参数。
- iconst_<i>: 定义常量。
- putfield:将栈顶的元素赋值。
- return : 表示返回。
了解了上面的方法结构,我们现在来解析每一个方法。
- 方法一,构造方法
-
00 04(methods_count) :方法个数为4,【这一句是复制上面的,重复方法属性】
-
00 01(access_flags) :表示是一个 public 方法
-
00 11(name_index):方法名称索引#17,为<init>表示构造方法。
-
00 12(descriptor_index):方法描述索引#18,为()V,表示传入参数个数为空,并且返回值为 void 类型
-
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 B7 00 01 2A 12 02 B5 00 03 2A 08 B5 00 04 B1:
0 aload_0(2A) :将位于0处的本地变量中的对象引用推到操作数堆栈上。
1 invokespecial #1 <java/lang/Object.>(B7 00 01):也就是调用父类 Object 的构造方法。
4 aload_0(2A):略
5 ldc #2 (12 02 ) :将字符串Welcome的引用推送到操作数堆栈上
7 putfield #3 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.str>( B5 00 03 ):将Welcome的引用从操作数堆栈中弹出,赋值给str
10 aload_0(2A):略
11 iconst_5 (08):转化为 int 类型
12 putfield #4 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.x>( B5 00 04 ):将当前操作数栈中的元素弹出到 x 变量,也就是给 x 变量赋值。
15 return(B1):抛出时,当前帧的操作数堆栈上的任何值都将被丢弃。然后解释器将控制权返回给方法的调用方,恢复调用方的框架。 -
00 00(exception_table_length) :异常表长度为0
-
00 02(attributes_count): 两个属性
-
00 14 :指向#20 为行号(LineNumberTable)表
-
00 00 00 0E(attribute_length):属性长度占14个字节
-
00 03 (line_number_table_length):行号表长度3
-
00 00 00 03 :源码中的第三行指向 aload_0
-
00 04 00 04:源码中的第4行指向 aload_0
-
00 0A 00 05 :源码中的第10行指向 ldc
-
00 15:指向常量池中#21,表示的是局部变量表 (LocalVariableTable)
-
00 00 00 0C:表示局部变量表所点字节个数 。这里是12。
-
00 01 00 00 00 10 00 16 00 17 00 00 :
- 00 01 :局部变量的个数为1
- 00 00 00 10:本地变量作用范围0-15
- 00 16 :本地变量表示 this
- 00 17 :this 的类型是 Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;
- 00 00:异常信息表长度为0
- main 方法
-
00 09 (Acess_Flag):方法由ACC_PUBLIC(0x0001),和ACC_STATIC(0x0008)组合而得来的。表示 public static
-
00 18 (name_index):方法名称索引#24,指向 main
-
00 19 ( descriptor_index):方法描述指向#25, ([Ljava/lang/String;)V,表示 传入的参数为 String[],并且返回值为 void 方法
-
00 01 (attributes_count):属性个数为1
-
00 13 (attribute_name_index):属性名称索引 #19,为 Code
-
00 00 00 57 (attribute_length):属性长度4个字节 ,总长度为87
-
00 02 (max_stack):方法运行的任何时刻所能达到的操作数栈的最大深度2
-
00 02(max_locals):方法执行期间创建的局部变量的数目2
-
00 00 00 17 (code_length):方法所包含的字节码的字节数以及具体的指令码,虚拟机所执行的字节码23
-
BB 00 05 59 B7 00 06 4C 2B 10 08 B6 00 07 10 14 B8 00 08 B3 00 09 B1:
- 0 new #5 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1>(BB 00 05):创建一个MyTest35_1对象
- 3 dup(59): 复制操作数堆栈上的顶部值,并将复制的值推送到操作数堆栈上。
- 4 invokespecial #6 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.>(B7 00 06):调用MyTest35_1的构造方法
- 7 astore_1(4C):操作数堆栈顶部的objectref必须是returnAddress类型或reference类型。它从操作数堆栈中弹出,1处的局部变量的值被设置为objectref。
- 8 aload_1(2B):加载刚刚存储的引用
- 9 bipush 8(10 08):将数字8推送到操作数堆栈上。
- 11 invokevirtual #7 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.setX>(B6 00 07):调用MyTest35_1的 setX 方法。
- 14 bipush 20(10 14):将数字20推送到操作数堆栈上。
- 16 invokestatic #8 <java/lang/Integer.valueOf>(B8 00 08):调用Integer.valueOf方法,自动装箱功能
- 19 putstatic #9 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.in>(B3 00 09):将栈顶的值赋值给MyTest35_1.in
- 22 return (B1):销毁操作数栈中数据
-
00 00 :异常表个数为0
-
00 02 (attributes_count):属性个数为2
-
00 14 (attribute_name_index):属性名称指向常量池中的#20,为行号表
-
00 00 00 12 (attribute_length):行号表长度占18个字节
-
00 04 00 00 00 09 00 08 00 0A 00 0E 00 0B 00 16 00 0C :
- 00 04:行号表中总共有4对映射
- 00 00 00 09 :源码中的第9行指向new MyTest35_1();
- 00 08 00 0A :源码中的第10行指向aload_1
- 00 0E 00 0B :源码中的11号指向bipush 8
- 00 16 00 0C : 源码中的第12行指向了22 ,return 掉了
-
00 15 :指向常量池中的#22 ,本地变量表LocalVariableTable
-
00 00 00 16 :本地变量表占22个字节
-
00 02 00 00 00 17 00 1A 00 1B 00 00 00 08 00 0F 00 1C 00 17 00 01:
- 00 02 :局部变量表的个数为2
- 00 00 00 17:局部变量表的作用范围从0-23
- 00 1A :局部变量表的名称#26,为args
- 00 1B:局部变量表的类型为[Ljava/lang/String;
- 00 00:第0个局部变量表
- 00 08 00 0F :第二个局部变量表的作用范围从【8 aload_1】开始
- 00 1C:局部变量表的名称指向#28 ,为myTest35_1
- 00 17:局部变量表的类型为#23,为Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;类型
- 00 01:第一个局部变量表。
到这里棰 main 方法己经解析完了。
- setX方法解析
-
00 01(access_flags): 表示是 public 方法
-
00 1D(name_index):方法名称指向#29,setX
-
00 1E (descriptor_index):方法描述,(I)V,返回值为 void 类型,传入一个 int 类型参数
-
00 01 :方法属性个数1
-
00 13:指向常量池中#19,表示方法的属性 Code。
-
00 00 00 3E(attribute_length):属性长度
-
00 02(max_stack):方法运行的任何时刻所能达到的操作数栈的最大深度2
-
00 02(max_locals):方法执行期间创建的局部变量的数目2
-
00 00 00 06:助记符长度占6个字节
-
2A 1B B5 00 04 B1:
- 0 aload_0(2A):将当前操作数栈推送到栈顶
- 1 iload_1(1B):1处的局部变量的值被推送到操作数堆栈上
- 2 putfield #4 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.x>(B5 00 04 ):弹出操作数栈中的值 ,并赋值给MyTest35_1的 x
- 5 return(B1): 销毁堆栈
-
00 00 :表示的是exception stack label table
-
00 02 :有两个属性
-
00 14:指向常量池#20,LineNumberTable
-
00 00 00 0A(attribute_length):属性长度占10个字节
-
00 02 00 00 00 0F 00 05 00 10 :
- 00 02 :行号表中有两行
- 00 00 00 0F:源码中的15行对应的是aload_0
- 00 05 00 10 :源码中的16行对应的是 return
-
00 15: 指向常量池中#21,而#21表示局部变量表。
-
00 00 00 16:局部变量表占22个字节
-
00 02 00 00 00 06 00 16 00 17 00 00 00 00 00 06 00 0D 00 0E 00 01:
- 00 02:局部变量表的个数为2
- 00 00 00 06:局部变量this的作用范围是0-6
- 00 16:局部变量表的名称指向常量池中#22,为this
- 00 17:局部变量表的类型为#23,Lcom/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1;
- 00 00 :索引为0
- 00 00 00 06 :局部变量的作用范围是0-6
- 00 0D:局部变量指向#13,为 x
- 00 0E :局部变量的类型指向#14,为 int
- 00 01 : 为第一个局部变量
- 静态方法
-
00 08(access_flags):方法为 static 类型
-
00 1F(name_index):方法的名称指向索引为#31,为<clinit>
-
00 12( descriptor_index):方法为void类型
-
00 01(attributes_count):属性个数为1
-
00 13(attribute_name_index) :为#19,这里是Code
-
00 00 00 21(attribute_length):属性长度33
-
00 01(max_stack): 方法运行的任何时刻所能达到的操作数栈的最大深度为2
-
00 00 (max_locals): 方法执行期间创建的局部变量的数目为0
-
00 00 00 09 (code_length):方法所包含的字节码的字节数以及具体的指令码9
-
10 0A B8 00 08 B3 00 09 B1:
- 0 bipush 10(10 0A):将10被推送到操作数堆栈上。
- 2 invokestatic #8 <java/lang/Integer.valueOf>(B8 00 08):调用Integer.valueOf方法
- 5 putstatic #9 <com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.in>(B3 00 09):赋值给MyTest35_1.in为10
- 8 return(B1):销毁堆栈
-
00 00 :异常堆栈表为0
-
00 01:只有一个属性
-
00 14 :指向常量池中的#20,表示LineNumberTable
-
00 00 00 06:行号表的长度占6个字节
-
00 01 00 00 00 06:
- 00 01:行号表中只有一行
- 00 00 00 06:源码中的第6行指向aload_0
- 00 01 00 20 00 00 00 02 00 21
- 00 01:属性个数为1
- 00 20 :指向常量池中的#32,为SourceFile
- 00 00 00 02 :源文件占2个字节,向后数两个字节。
- 00 21:指向常量池#33,当前源文件的名称为MyTest35_1.java
本文的 github 地址是
https://github.com/quyixiao/spring_tiny/blob/master/src/main/java/com/spring_1_100/test_31_40/test35_resource_inject/MyTest35_1.java