Java字节码可以以非文件的其它方式存在:
理解java字节码文件重要的几个属性:
-
字节码文件信息(ClassFile结构)
-
描述符(descriptor)
-
类常量池(Constant pool)
-
访问标志(flags)
-
Code属性
-
ConstantValue
-
LineNumberTables
-
Exception table
-
StackMapTable
-
InnerClasses
ClassFile结构
类型 | 名称 | 数量 | 含义 |
u4 | magic | 1 | 魔数值固定为0xCAFEBABE |
u2 | minor_version | 1 | 副版本号 |
u2 | major_version | 1 | 主版本号 |
u2 | constant_pool_count | 1 | 常量池计数器,值等于常量池表中的成员数加1。8字节常量占两个表成员空间计数为加1再加1。 |
cp_info | constant_pool | constant_pool_count - 1 | 常量池 字符串常量、类或接口名、字段名和其它常量。索引从1开始,0固定为this或空 |
u2 | access_flags | 1 | 类或者接口的访问权限及基础属性 |
u2 | this_class | 1 | 类索引 |
u2 | super_class | 1 | 父类索引 |
u2 | interfaces_count | 1 | 接口计数器 |
u2 | interfaces | interfaces_count | 接口表 |
u2 | fields_count | 1 | 字段计数器 |
field_info | fields | fields_count | 字段表 |
u2 | methods_count | 1 | 方法计数器 |
method_info | methods | methods_count | 方法表 |
u2 | attribute_count | 1 | 属性计数器 |
attribute_info | attributes | attributes_count | 属性表 |
Java虚拟机规范定义了u1、u2、u4和u8来分别表示1个字节、2个字节、4个字节和8个字节的无符号数,分别对应于byte、short、int和long。
-
attribute_name_index u2
-
attribute_length u4 固定为2
-
soucefile_index u2 UTF-8常量索引
☞注:都是tag + 长度 +内容 以此分析字节码文件
类常量池(Constant pool)
常量池中数据项类型 | 类型标志 | 类型描述 |
CONSTANT_Utf8 | 1 | UTF-8编码的Unicode字符串 |
CONSTANT_Integer | 3 | int类型字面值 |
CONSTANT_Float | 4 | float类型字面值 |
CONSTANT_Long | 5 | long类型字面值 |
CONSTANT_Double | 6 | double类型字面值 |
CONSTANT_Class | 7 | 对一个类或接口的符号引用 |
CONSTANT_String | 8 | String类型字面值 |
CONSTANT_Fieldref | 9 | 对一个字段的符号引用 |
CONSTANT_Methodref | 10 | 对一个类中声明的方法的符号引用 |
CONSTANT_InterfaceMethodref | 11 | 对一个接口中声明的方法的符号引用 |
CONSTANT_NameAndType | 12 | 对一个字段或方法的部分符号引用 |
常量池之中主要存放两大类常量:
1).字面量: 比较接近于Java语言层面的常量概念,如文本字符串、被声明为final的常量值等
2).符号引用: 属于编译原理方面的概念,包括了下面三类常量:
①.类和接口的全限定名
②.字段的名称和描述符
③.方法的名称和描述符
访问标志(flags)
Flag Name | 值 | 解释 |
ACC_PUBLIC | 0x0001 | public |
ACC_FINAL | 0x0010 | final,不允许有子类 |
ACC_SUPER | 0x0020 | 跟调用invokespecial指令时的处理有关 |
ACC_INTERFACE | 0x0200 | 这是个接口 |
ACC_ABSTRACT | 0x0400 | abastract不能实例化 |
ACC_SYNTHETIC | 0x1000 | 没有在源代码中出现,该类是编译器生成的 |
ACC_ANNOTATION | 0x2000 | 注解类型 |
ACC_ENUM | 0x4000 | 枚举类型 |
描述符(descriptor)
符号 | 类型 | 含义 |
B | byte | 有符号的字节型数 |
C | char | |
D | double | |
F | float | |
I | int | |
J | long | |
L ClassName; | reference | ClassName类实例 |
S | short | |
Z | boolean | |
[ | reference | 一个一维数组 |
V | void | 方法描述符(参数类别)V,表示不返回 |
Code属性
在field和method中,可以有若干个attribute,类文件也有attribute,用于描述一些额外的信息
attribute_name_index u2 名字,指向常量池UTF-8
attribute_length u4 长度
info[attribute_length] u1内容
Deprecated 描述
-
attribute_name_index u2 指向常量池表示Deprecated的UTF-8常量
-
attribute_length u4 为0
ConstantValue 描述
-
attribute_name_index u2 包含ConstantantValue字面量的UTF-8索引
-
attribute_length u4 为2
-
constantvalue_index u2 常量值,指向常量池,可以是UTF-8,Float, Double
Code 使用结构体来描述
-
Code_attribute {
-
u2 attribute_name_index; //指向utf-8 code常量
-
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; /*处理的异常类型,
-
指向常量池表示constant_class的utf-8常量*/
-
} exception_table[exception_table_length];
-
u2 attributes_count; //属性数量
-
/*对其他属性的描述如lineNumberTable、
-
LocalVariableTable 属性*/
-
attribute_info attributes[attributes_count];
-
}
LineNumberTable - Code属性的属性
-
LineNumberTable_attribute {
-
u2 attribute_name_index;//指向常量池LineNumberTable的utf-8常量
-
u4 attribute_length;//长度
-
u2 line_number_table_length;//多少个表项
-
-
{ u2 start_pc; //字节码偏移量
-
u2 line_number; //对应行号
-
} line_number_table[line_number_table_length];
-
}
LocalVariableTable - Code属性的属性(调试信息)
-
LocalVariableTable_attribute {
-
u2 attribute_name_index;//指向常量池表示LocalVariableTable
-
的utf-8常量
-
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];
-
}
调试器在执行方法的过程中,用它来确定某个局部变量的值。
LocalVariableTable Attribute用于调试器,以获取在方法运行时局部变量的信息。在一个Code Attribute中只包含1或0项LocalVariableTable Attribute。
LocalVariableTypeTable Code属性的属性(调试信息)
-
LocalVariableTypeTable _attribute {
-
u2 attribute_name_index;//指向常量池表示LocalVariableTable
-
的utf-8常量
-
u4 attribute_length;
-
u2 local_variable_table_length;
-
-
{ u2 start_pc; //局部变量偏移量
-
u2 length;
-
u2 name_index; //局部变量名称 指向常量池索引
-
u2 signature_index; //局部变量类型 指向常量池索引
-
u2 index; //局部变量位置
-
} local_variable_type_table[local_variable_type_table_length];
-
}
调试器在执行方法的过程中,用它来确定某个运行时泛型局部变量的值。
LocalVariableTable Attribute和LocalVariableTypeTable Attribute表达的信息是类似的,他们的区别是对泛型类型的局部变量,需要用Signature的形式表达,而不能仅仅用Descriptor的形式表达,因而对泛型类型的局部变量,需要在LocalVariableTable Attribute和LocalVariableTypeTable Attribute中同时存在一项;而对非泛型类型的局部变量来说,只要在LocalVariableTable Attribute存在表项就可以了。
Exception table属性
描述异常类的信息,类似code,是throws部分不是try catch部分
-
attribute_name_index u2
-
attribute_length u4
-
number_of_exceptions u2
-
exception_index_table[number_of_exceptions] u2
-
指向Constant_Class的索引
StackMapTable
在 Java 6 版本之后 JVM 引入了栈图 (Stack Map Table) 概念。为了提高验证过程的效率,在字节码规范中添加了 Stack Map Table 属性,以下简称栈图,其方法的 code 属性中存储了局部变量和操作数的类型验证以及字节码的偏移量。也就是一个 method 需要且仅对应一个 Stack Map Table 。在 Java 7 版本之后把栈图作为字节码文件中的强制部分。
参考:https://hllvm-group.iteye.com/group/topic/26545
/*same */中same表示帧类型,即same_frame。
根据frame_type的值计算offset_delta(偏移量增量)。不同的帧类型的偏移量增量取值范围和计算方式不同。计算字节码偏移量:前一帧的字节码偏移量加上offset_delta的值再加1。
InnerClasses
后面的注释代表前面引用的值。