3. 访问标志
Access_Flag 访问标志 访问标识信息包括该Class文件时类和接口是否被定义成了public,是否是 abstract, 如果是类,是否被申明为成final。通过扇面的源代码。
0x 00 21
: 表示是0x0020 和0x0001的并集, 表示 ACC_PUBLIC
与 ACC_SUPER
4. 类索引、父类索引
00 03
类名, 03 常量池位置 cn/edu/cqvie/jvm/bytecode/TestClass
00 04
父类名. 04 常量池位置 java/lang/Object
00 00
接口个数, 0 个, 接口个数最多 65535
5. 字段表集合
00 01
字段的个数, 这里有一个
字段表结构
field_info { u2 access_flags; 00 02 Fieldref u2 name_index; 00 05 (表示字段名称在常量池中的索引位置) m u2 descriptor_index; 00 06 (描述符索引) I u2 attributes_count; 00 00 attribute_info attributes[attributes_count] }
6. 方法表集合
00 02
表示有两个方法
方法表结构
method_info { u2 access_flags; 00 01 Methodref u2 name_index; // 00 07 <init> u2 descriptor_index; 00 08 // ()V u2 attributes_count; 00 01 // 属性结构 attributes_info attributes[attributes_count] }
方法属性结构
attribute_info { u2 attribute_name_index; 00 09 // Code u4 attribute_length; 00 00 00 1D 长度 29 u1 info[attribute_length]; ... }
7. 属性表集合
00 09 00 00 1D 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0A 00 00 00 06 00 01 00 00 00 03 00
"Code" 表示下面是执行代码
JVM 预定义了一部分的attribute, 但是编译器自己也可以实现自己的attribute 写入class文件中, 供运行时使用。 不同的attribute 通过attribute_name_index 来区分。
- JVM 规范预定义的attribute
- Code 结构
Code attribute 的作用是保存该放的的结构,如所对应的字节码
Code_attribute { u2 attribute_name_index; // 00 09 ==> Code u4 attribute_length; // 00 00 00 1D ==> 29 u2 max_stack; // 00 01 栈深度为 1 (栈帧中操作数栈的深度) u4 code_length; // 00 00 00 05 指令的长度是多少 u1 code[code_lenght]; // 2A B7 00 01 B1 其中 2A // 2A aload_0 // B7 invokespecial // 00 #1 // 01 <java/lang/Object.<init>> // B1 return // 0 aload_0 // 1 invokespecial #1 <java/lang/Object.<init>> // 4 return u2 exception_table_length; // 00 00 { u2 start_pc; u2 end_pc; u2 handler_pc; u2 catch_type; } exception_table[exception_table_lenght]; u2 attributes_count; // 00 01 attribute_info attributes[attributes_count]; }
- attribute_length 表示 attribute 所包含的字节数, 不包含attribute_name_index 和 attribute_length 字段。
- max_stack 表示这个方法运行的任何时刻锁能达到的操作数栈的最大深度。
- max_locals 表示方法执行期间创建的局部变量的数目,包含用来表示传入的参数的局部变量。
- code_length 表示该方法所包含的字节码的字节数以及具体的指令码。
- 具体字节码即是该方法被调用时,虚拟机所执行的字节码。
- exception_table, 这里存放的是处理异常信息。
- 每个 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 的结构
LineNumberTable_attribute { u2 attribute_name_index; //00 0A 常量池10号位置 LineNumberTable u4 attribute_lenght; // 00 00 00 06 一共 6个长度 u2 line_number_table_length; // 00 01 映射的对数 1 对 line_number_info { u2 start_pc; // 指令行号 00 00 u2 line_number; // 源码调试 00 03 } line_number_table[line_number_table_length]; }
局部变量表 LocalVariableTable
LocalVariableTable_attribute { u2 attribute_name_index; u4 attribute_lenght; u2 local_variable_table_length; { u2 start_pc; u2 line_number; u2 name_index; u2 descriptor_index; u2 index; } }
总结
- 构造方法中会初始化成员属性的默认值,如果自己实现了默认的构造方法, 依然还是在构造方法中赋值,这就是对指令的重排序。
- 如果多个构造方法那么每个构造方法中都有初始化成员变量的属性,来保障每个构造方法初始化的时候都会执行到属性的初始化过程。
- 如果构造方法中有执行语句, 那么会先执行赋值信息, 然后在执行自定义的执行代码。
- 对于Java每一个实例方法(非静态方法), 其在编译后生成的字节码中比实际方法多一个参数, 它位于方法的第一个参数位置. 我们就可以在当前方法中 的this去访问当前对象中的this这个操作是在Javac 编译器在编译期间将this的访问转换为对普通实例方法的参数访问,接下来在运行期间, 由JVM的调用实例方法时, 自动向实例方法中传入该this参数, 所以在实例方法的局部变量表中, 至少会一个指向当前对象的局部变量。
- 字节码对于处理异常的方式:
- 统一采用异常表的方式来对异常处理。
- 在Jdk1.4.2之前的版本中, 并不是使用异常表的方式来对异常进行处理的,而是采用特定的指令方式。
- 当异常处理存在finally 语句块时,现代化的JVM才去的处理方式将finally语句块的自己吗拼接到每一个catch块后面, 换句话说,程序中中存在多个catch块,就会在每一个catch块后面重复多少个finally 的语句块字节码。