bilibili-JVM学习笔记12 - 解读笔记11中的attributes
The Java Virtual Machine Specification - Java SE 8 Edition
4.7. Attributes
附加属性
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
Table 4.7-A. Predefined class file attributes (by section)
Attribute | Section | class file | Java SE |
---|---|---|---|
ConstantValue | §4.7.2 | 45.3 | 1.0.2 |
Code | §4.7.3 | 45.3 | 1.0.2 |
StackMapTable | §4.7.4 | 50.0 | 6 |
Exceptions | §4.7.5 | 45.3 | 1.0.2 |
InnerClasses | §4.7.6 | 45.3 | 1.1 |
EnclosingMethod | §4.7.7 | 49.0 | 5.0 |
Synthetic | §4.7.8 | 45.3 | 1.1 |
Signature | §4.7.9 | 49.0 | 5.0 |
SourceFile | §4.7.10 | 45.3 | 1.0.2 |
SourceDebugExtension | §4.7.11 | 49.0 | 5.0 |
LineNumberTable | §4.7.12 | 45.3 | 1.0.2 |
LocalVariableTable | §4.7.13 | 45.3 | 1.0.2 |
LocalVariableTypeTable | §4.7.14 | 49.0 | 5.0 |
Deprecated | §4.7.15 | 45.3 | 1.1 |
RuntimeVisibleAnnotations | §4.7.16 | 49.0 | 5.0 |
RuntimeInvisibleAnnotations | §4.7.17 | 49.0 | 5.0 |
RuntimeVisibleParameterAnnotations | §4.7.18 | 49.0 | 5.0 |
RuntimeInvisibleParameterAnnotations | §4.7.19 | 49.0 | 5.0 |
RuntimeVisibleTypeAnnotations | §4.7.20 | 52.0 | 8 |
RuntimeInvisibleTypeAnnotations | §4.7.21 | 52.0 | 8 |
AnnotationDefault | §4.7.22 | 49.0 | 5.0 |
BootstrapMethods | §4.7.23 | 51.0 | 7 |
MethodParameters | §4.7.24 | 52.0 | 8 |
4.7.3. The 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_length];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
4.7.12. The LineNumberTable Attribute
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
4.7.13. The LocalVariableTable Attribute
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc;
u2 length;
u2 name_index;
u2 descriptor_index;
u2 index;
} local_variable_table[local_variable_table_length];
}
解析 method_info[0]
- access_flags u2
0001
0007 --> 查询 Table 4.6-A --> = 0x0001 --> ACC_PUBLIC
- name_index u2
- 0001
0007
--> 指向常量池索引 7 --> --> 表示此方法为构造方法
- 0001
- descriptor_index u2
0008
0001 --> 指向常量池索引 8 --> ()V --> () 内无参数表示此构造方法为无参构造器
- attributes_count u2
- 0008
0001
--> 1 个 attribute_info
- 0008
- attributes[attributes_count] attribute_info
- attributes[0]
- attribute_name_index u2
0009
0000 --> 指向常量池索引 9 --> Code
- attribute_length u4
- 0009
0000 0038
0002 --> 长度 56
- 0009
- info[attribute_length] u1
- 长度 * u1
- 0038
0002 00010000 000A2AB7 00012A04 B50002B1 00000002 000A0000 000A0002 00000003 00040005 000B0000 000C0001 0000000A 000C000D 0000
0001 --> 详细信息如下: - max_stack u2
- 0038
0002
--> 2 --> max_stack 项的值给出该方法执行过程中任意时刻该方法的操作数堆栈的最大深度(第2.6.2节)
- 0038
- max_locals u2
0001
0000 --> 1 --> max_locals 项目的值给出在调用此方法(第2.6.1节)时分配的局部变量数组中的局部变量数,包括用于在调用该方法时将参数传递给该方法的局部变量。long 或 double 类型的值的最大局部变量索引是 max_locals-2。max_locals-1 是任何其他类型的值的最大局部变量索引。
- code_length u4
- 0001
0000 000A
2AB7 --> 10 --> code_length 项的值给出此方法在代码数组中的字节数。code_length的值必须大于零(因为代码数组不能为空)且小于65536。
- 0001
- code[code_length] u1
- 000A
2AB7 00012A04 B50002B1
--> 解析成指令集- 0x2A --> 查询jvm指令集 --> aload_0
- 没有参数
- 0xB7 --> invokespecial
- 有一个参数 0001 --> #1 -->
java/lang/Object."<init>":()V
- 有一个参数 0001 --> #1 -->
- 0x2A --> aload_0
- 0x04 --> 0x4 --> iconst_1
- 0xB5 --> putfield
- 0002 --> #2 --> new_package/jvm/p37/MyTest1.a:I
- 0xB1 --> return
- 0x2A --> 查询jvm指令集 --> aload_0
- 000A
- exception_table_length u2
0000
0002 --> 0
- exception_table {…}
- attributes_count u2
- 0000
0002
--> 2 个附加属性
- 0000
- attribute_info attributes[attributes_count]
- attributes[0]
- attribute_name_index u2
000A
0000 --> 10 --> LineNumberTable
- attribute_length u4
- 000A
0000 000A
0002 --> 10
- 000A
- info[attribute_length] u1
- 000A
0002 00000003 00040005
-->LineNumberTable Attribute
- line_number_table_length u2
- 000A
0002
--> 2
- 000A
- line_number_table[line_number_table_length]
- line_number_table[0]
- start_pc u2
0000
0003 --> 0
- line_number u2
- 0000
0003
--> 3
- 0000
- start_pc u2
- line_number_table[1]
- start_pc u2
0004
0005 --> 4
- line_number u2
- 0004
0005
--> 5
- 0004
- start_pc u2
- line_number_table[0]
- 000A
- attribute_name_index u2
- attributes[1]
- attribute_name_index u2
000B
0000 --> 11 --> LocalVariableTable
- attribute_length u4
- 000B
0000 000C
0001 --> 12
- 000B
- info[attribute_length] u1
- 000C
0001 0000000A 000C000D 0000
0001 -->LocalVariableTable Attribute
- local_variable_table_length u2
- 000C
0001
--> 1
- 000C
- local_variable_table[local_variable_table_length]
- local_variable_table[0]
- start_pc u2
0000
000A --> 0
- length u2
- 0000
000A
--> 10
- 0000
- name_index u2
000C
000D --> this
- descriptor_index u2
- 000C
000D
--> Lnew_package/jvm/p37/MyTest1;
- 000C
- index u2
0000
0001 --> 0
- start_pc u2
- local_variable_table[0]
- 000C
- attribute_name_index u2
- attribute_name_index u2
解析 method_info[1]
- access_flags u2
- 0000
0001
--> 查询 Table 4.6-A --> = 0x0001 --> ACC_PUBLIC
- 0000
- name_index u2
000E
000F --> 指向常量池索引 14 --> getA --> 方法名
- descriptor_index u2
- 000E
000F
--> 指向常量池索引 15 --> ()I --> 方法描述符
- 000E
- attributes_count u2
0001
0009 --> 1 个 attribute_info
- attributes[attributes_count] attribute_info
- attributes[0]
- attribute_name_index u2
- 0001
0009
--> 指向常量池索引 9 --> Code
- 0001
- attribute_length u4
0000002F
--> 长度 47
- info[attribute_length] u1
- 长度 * u1
00010001 00000005 2AB40002 AC000000 02000A00 00000600 01000000 08000B00 00000C00 01000000 05000C00 0D0000
00 -->- max_stack u2
0001
0001 --> 1
- max_locals u2
- 0001
0001
--> 1
- 0001
- code_length u4
00000005
--> 5
- code[code_length] u1
2AB40002 AC
000000- 0x2A --> aload_0
- 0xB4 --> getfield
- 0002 --> #2 --> new_package/jvm/p37/MyTest1.a:I
- 0xAC --> ireturn
- exception_table_length u2
- AC
0000
00 --> 0
- AC
- exception_table {…}
- attributes_count u2
- AC0000
00 02
000A00 --> 2 个附加属性
- AC0000
- attribute_info attributes[attributes_count]
- attributes[0]
- attribute_name_index u2
- 02
000A
00 --> 10 --> LineNumberTable
- 02
- attribute_length u4
- 02000A
00 000006
00 --> 6
- 02000A
- info[attribute_length] u1
- 000006
00 01000000 08
000B00 -->LineNumberTable Attribute
- line_number_table_length u2
- 000006
00 01
000000 --> 1
- 000006
- line_number_table[line_number_table_length]
- line_number_table[0]
- start_pc u2
- 01
0000
00 --> 0
- 01
- line_number u2
- 010000
00 08
000B00 --> 8
- 010000
- start_pc u2
- line_number_table[0]
- 000006
- attribute_name_index u2
- attributes[1]
- attribute_name_index u2
- 08
000B
00 --> 11 --> LocalVariableTable
- 08
- attribute_length u4
- 08000B
00 00000C
00 --> 12
- 08000B
- info[attribute_length] u1
- 00000C
00 01000000 05000C00 0D0000
00 -->LocalVariableTable Attribute
- local_variable_table_length u2
- 00000C
00 01
000000 --> 1
- 00000C
- local_variable_table[local_variable_table_length]
- local_variable_table[0]
- start_pc u2
- 01
0000
00 --> 0
- 01
- length u2
- 010000
00 05
000C00 --> 5
- 010000
- name_index u2
- 05
000C
00 --> this
- 05
- descriptor_index u2
- 05000C
00 0D
000000 --> Lnew_package/jvm/p37/MyTest1;
- 05000C
- index u2
- 0D
0000
00 --> 0
- 0D
- start_pc u2
- local_variable_table[0]
- 00000C
- attribute_name_index u2
- attribute_name_index u2
资料
- 方法(method_info)中的每个属性都是一个 attribute_info 结构;
- 方法属性结构
- JVM 预定义了部分 attribute ,但是编译器自己也可以实现自己的 attribute 写入 class 文件里,供运行时使用;
- JVM 预定义的 attribute
- ConstantValue
- Code
- StackMapTable
- Exceptions
- …
- 不同的 attribute 通过 attribute_name_index 来区分
- JVM 预定义了部分 attribute ,但是编译器自己也可以实现自己的 attribute 写入 class 文件里,供运行时使用;
- Code 属性结构
- attribute_name_index 名称
- 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{…})
- catch_type 表示会被处理的异常类型,它指向常量池里的一个异常类,当 catch_type 为 0 时,表示处理所有的异常
工具
字节码查看工具
- jclasslib
- https://github.com/ingokegel/jclasslib
- idea 插件:jclasslib
- View -> Show Bytecode With jclasslib