Java字节码内容的那些表

表格参考来自于 “深入理解Java虚拟机:JVM高级特定及最佳实践”

Class文件结构表

类型 名称 描述 数量
u4(4个字节) magic 确定该文件是否为一个能被虚拟机接受的Class文件,类似于ID 1
u2(2个字节) minot_version 次版本号 1
u2(2个字节) mahor_version 主版本号 1
u2(2个字节) constant_pool_count 常量池容量计数值,从1开始计算,0则表示不引用任何一个常量池项目 1
cp_info constant_pool 常量池 constant_pool_count-1
u2(2个字节) access_flags 访问标志 1
u2(2个字节) this_class 类索引 1
u2(2个字节) super_class 父类索引 1
u2(2个字节) interfaces_count 实现接口的数目 1
u2(4个字节) interfaces 接口索引 interfaces_count
u2(4个字节) fields_count 字段的数目 1
field_info fields 字段内容 fields_count
u2(2个字节) methods_count 方法的数目 1
method_info methods 方法内容 methods_count
u2(2个字节) attributes_count 属性的数目 1
attribute_info attributes 属性内容 attributes_count

常量表

常量池主要存放两种类型

  • 字面量,包含文本字符串,final的常量值等

  • 符号引用,类和接口的全限定名,字段的名称和描述符,方法的名称和描述符

Class文件只保存各个方法,字段端的信息,不保存内存信息。只有经过运行期转换才能得到真正的内存入口。当虚拟机运行时,需要从常量池中获取到对应的符号引用,再经过类创建或者运行时解析,得到具体的内存地址。

类型 子结构 标志 描述
CONSTANT_Utf8_info tag u1 = 1 UTF-8编码的字符串
- lenght u2 UTF-8编码的字符串占用的字节数
- bytes u1 长度为lenght的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 类或接口的符号引用
- bytes u2 指向全限定名常量项的索引
CONSTANT_String_info tag u1=8 字符串类型字面量
- bytes u2 指向字符串字面量的索引
CONSTANT_Fieldref_info tag u1=9 字段的符号引用
- index u2 指向声明字段的类或者接口描述符 CONSTANT_Class_info 的索引项
- index u2 指向声明字段的类或者接口描述符CONSTANT_NameAndType_info 的索引项
CONSTANT_Methodred_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 指向该字段或方法名称常量项的索引
CONSTANT_MethodHandle_info tag u1=15 表示方法句柄
- reference_kind u1 值必须在[1,9]中,它决定了方法句柄的类型。方法句柄类型的值表示方法句柄的字节码行为
- reference_index u2 值必须是对常量池的有效索引
CONSTANT_MethodType_info tag u1=16 识别方法类型
- descriptor_index u2 值必须是对常量池的有效索引,常量池在该索引处的项必须是CONSTANT_Utf8_info结构,表示方法的描述符
CONSTANT_InvokeDynamic_info tag u1=18 表示一个动态方法调用点
- bootstrap_method_attar_index u2 值必须是对当前Class文件中引导方法表的 bootstrap_methods[]数组的有效索引
- name_and_type_index u2 值必须是对当前常量池的有效索引,常量池在该索引处的值必须是CONSTANT_NameAndType_info结构,表示方法名和方法描述符

访问标志表

针对类,字段表,方法表中的访问标志进行划分。

类访问标志

用于识别一些类或者接口层次的访问信息, 包括这个Class是类还是接口,是否被定义成public类型,是否被定义成abstract类类型,如果是类的话,是否被声明为final等等

标志名称 标志值 描述
ACC_PUBLIC 0x0001 是否为public类型
ACC_FINAL 0x0010 是否被声明为final,只有类可设置
ACC_SUPER 0x0020 是否允许使用invokespecial字节码指令的新语意,invokespecial指令的语意在JDK1.0.2发生过变化,为了区别这条指令使用哪种语意,JDK1.0.2之后编译出来的类的这个标识必须都为真
ACC_INTERFACE 0x0200 标识这个是一个接口
ACC_ABSTRACT 0x0400 是否为abstract类型,对于接口或者抽象类来说,此标志的值都为真,其他类型为假
ACC_SYNTHETIC 0x1000 标识这个类并非由用户代码产生的
ACC_ANNOTATION 0x2000 标识这是一个注解
ACC_ENUM 0x4000 标识这是一个枚举

内部类访问标志

标志名称 标志值 描述
ACC_PUBLIC 0x0001 内部类是否为public
ACC_PRIVATE 0x0002 内部类是否为private
ACC_PROTECTED 0x0004 内部类是否为protected
ACC_STATIC 0x0008 内部类是否为protected
ACC_FINAL 0x0010 内部类是否为protected
ACC_INTERFACE 0x0020 内部类是否为接口
ACC_ABSTRACT 0x0400 内部类是否为abstract
ACC_SYNTHETIC 0x1000 内部类是否并非由用户代码产生
ACC_ANNOTATION 0x2000 内部类是否是一个注解
ACC_ENUM 0x4000 内部类是否是一个枚举

字段访问标志

标志名称 标志值 描述
ACC_PUBLIC 0x0001 字段是否为public
ACC_PRIVATE 0x0002 字段是否为private
ACC_PROTECTED 0x0004 字段是否为protected
ACC_STATIC 0x0008 字段是否为static
ACC_FINAL 0x0010 字段是否为final
ACC_VOLATILE 0x0040 字段是否为volatile
ACC_TRANSIENT 0x0080 字段是否为transient
ACC_SYNTHETIC 0x1000 字段是否由编译器自动产生的
ACC_ENUM 0x4000 字段是否为enum

方法访问标志

标志名称 标志值 描述
ACC_PUBLIC 0x0001 方法是否为public
ACC_PRIVATE 0x0002 方法是否为private
ACC_PROTECTED 0x0004 方法是否为protected
ACC_STATIC 0x0008 方法是否为static
ACC_FINAL 0x0010 方法是否为final
ACC_SYNCHRONIZED 0x0020 方法是否为synchronized
ACC_BRIDGE 0x0040 方法是否由编译器产生的桥接方法
ACC_VARARGS 0x0080 方法是否接受不定参数
ACC_NATIVE 0x0100 方法是否为native
ACC_ABSTRACT 0x0400 方法是否为abstract
ACC_STRICTFP 0x0800 方法是否为strictfp
ACC_SYNTHETIC 0x1000 方法是否由编译器自动产生的

字段表

用于描述接口和类中声明的变量,包括类级别变量以及实例级别变量

类型 名称 数量
u2 access_flags 1
u2 name_index 1
u2 descriptor_index 1
u2 attributes_count 1
u2 attributes attributes_count

其中 access_flags 见上面访问标志表中的字段访问标志

方法表

方法表包含访问标志,名称索引和描述符索引,属性表结合等几项

类型 名称 数量
u2 access_flags 1
u2 name_index 1
u2 descriptor_index 1
u2 attributes_count 1
attribute_info attributes attributes_count

其中方法的access_flags见上述的方法访问标志

属性表

属性表用于解释Class文件,字段表,方法表中携带的属性表集合,用于描述某些场景专有的信息。

属性名称 使用位置 含义
Code 方法表 Java代码编译成的字节码指令
ConstantValue 字段表 final关键字定义的常量值
Deprecated 类,方法表,字段表 final关键字定义的常量值
Exceptions 方法表 final方法抛出的异常
EnclosingMethod 类文件 仅当一个类为局部类或者匿名类时才能拥有这个属性,这个属性用于标识这个类所在的外围方法
InnerClasses 类文件 内部类列表
LineNumberTable Code属性 Java源码的行号与字节码指令的对应关系
LocalVariableTable Code属性 方法的局部变量描述
StackMapTable Code属性 JDK1.6中新增的属性,供新的类型检查校验器(Type Checker)检查和处理目标方法的局部变量和操作数栈锁需要的类型是否匹配
Signature 类,方法表,字段表 JDK1.5中新增的属性,这个属性用于支持泛型情况下的方法签名,在java语言中,任何类,接口,初始化方法或成员的泛型签名如果包含了类型变量(Type Variables)或者参数化类型(Parameterized Types),则Signature属性会为它记录泛型签名信息。由于java的泛型采用擦除法实现,在为了类型信息被擦除后导致签名混乱,需要这个属性记录泛型中的相关信息
SourceFile 类文件 记录源文件名称
SourceDebugExtension 类文件 JDK1.6中新增的属性,SourceDebugExtension属性用于存储额外的调试信息。譬如在进行JSP文件调试时,无法通过Java堆栈来定位到JSP文件的行号,JSR-45规范为这些非Java语言编写,却需要编译成字节码并运行在Java虚拟机中的程序提供了一个进行调试的标准机制,使用SourceDebugExtension属性就可以用于存储这个标准所新加入的调试信息
Synthetic 类,方法表,字段表 标识方法或者字段是否为编译器自动生成的
LocalVariableTypeTable JDK1.5中新增的属性,它使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加的
RuntimevisibleAnnotations 类,方法表,字段表 JDK1.5中新增的属性,为动态注解提供支持。RuntimevisibleAnnotations 属性用于指明哪些注解是运行时(实际上运行时就是进行反射调用)可见的
RuntimeInvisibleAnnotations 类,方法表,字段表 JDK1.5中新增的属性,与 RuntimevisibleAnnotations 属性作用刚好相反, 用于指明哪些注解是运行时不可见的
RuntimeVisibleParameterAnnotations 方法表 JDK1.5中新增的属性,作用与 RuntimevisibleAnnotations 属性类似,只不过作用对象为方法参数
RuntimeInvisibleParameterAnnotations 方法表 JDK1.5中新增的属性,作用与 RuntimeInvisibleAnnotations 属性类似,只不过作用对象为方法参数
AnnotationDetault 方法表 JDK1.5中新增的属性,用于记录注解类元素的默认值
BootstrapMethods 类文件 JDK1.5中新增的属性,用于保存 invokedynamic 指令引用的引导方法限定符

上述的每一个属性,都需要从常量池中引用一个 CONSTANT_Utf8_info类型常量来标示。还包含attribute_length(u4)用于标示属性值所占用的位数,后面再跟着属性内容。

Code属性结构

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 max_stack 1
u2 max_locals 1
u4 code_length 1
u1 code code_lenght
u2 exception_table_lenght 1
exception_info exception_table exception_table_length
u2 attributes_count 1
attribute_info attributes attributes_count

异常属性结构

类型 名称 数量
u2 start_pc 1
u2 end_pc 1
u2 handler_pc 1
u2 catch_type 1

Exceptions属性结构

区别与异常表,该表主要是列举中方法中可能抛出的受检查异常,也就是方法描述时throws关键字列举的异常

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 number_of_exceptions 1
u2 exception_index_table number_of_exceptions

LineNumberTable属性结构

用于描述Java源码行号与字节码行号之间的对应关系,默认声称到Class文件中。

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 line_number_table_length 1
line_number_info line_number_table line_number_table_length

其中line_number_info包含start_pc和line_number两个u2类型的数据项。

LocalVariableTable属性结构

用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,默认生成到Class文件中

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 local_variable_table_lenght 1
local_variable_info local_variable_table local_variable_table_lenght

其中 local_variable_info是代表栈帧与源码中局部变量的关联,见下表

类型 名称 含义 数量
u2 start_pc 局部变量的生命周期开始的字节码偏移量 1
u2 length 局部变量的生命周期开始的作用范围覆盖长度 1
u2 name_index 指向常量池 CONSTANT_Utf8_info 索引 1
u2 descriptor_index 指向常量池 CONSTANT_Utf8_info 索引 1
u2 index 局部变量在栈帧局部变量表中Slot的位置 1

SourceFile属性结构

用于记录生成这个Class文件的源码文件名称

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 sourcefile_index 1

其中 sourcefile_index为指向常量池 CONSTANT_Utf8_info 索引

ConstantValue属性结构

用于通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量才可以使用这项属性。

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 constant_index 1

InnerClasses属性结构

用于记录内部类与宿主类之间的关联,如果一个类中定义了内部类,编译器则会为它生成内部类INnerClasses属性

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 number_of_classes 1
inner_classes_info inner_classes number_of_classes

每一个inner_classes_info代表一个内部类信息,结构如下

类型 名称 含义 数量
u2 inner_class_info_index 指向常量池 CONSTANT_Class_info 索引 1
u2 outer_class_info_index 指向常量池 CONSTANT_Class_info 索引 1
u2 inner_name_index 指向常量池 CONSTANT_Utf8_info 索引,代表这个内部类的名称,如果匿名则为0 1
u2 inner_class_access_flags 内部类的访问标志,见上述访问标志篇章 1

Deprecated/Synthetic属性结构

前者是用于标示某个类,字段或者方法是否不再推荐使用

后者是用于标示字段或者方法不是由Java源码直接产生,所有由非用户代码生成的方法都需要设置Synthetic属性或者ACC_SYNTHETIC标志,但是除外。他们的结构如下

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1

StackMapTable属性结构

于JDK1.6之后添加在Class规范中,位于Code属性表中,该属性会在虚拟机类加载的字节码校验阶段被新类型检查检验器(Type Checker)使用。

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 number_of_entries 1
stack_map_frame stack_map_frame_entries number_of_entries

Signature属性结构

于JDK1.5发布之后添加到Class规范中,它是一个可选的定长属性,可以出现在类,属性表,方法表结构的属性表中。该属性会记录泛型签名信息,在Java语言中泛型采用的是擦除法实现的伪泛型,在字节码(Code属性)中,泛型信息编译之后都统统被擦除掉。由于无法像C#等运行时支持获取真泛型类型,添加该属性用于弥补该缺陷,现在Java反射已经能获取到泛型类型。

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 signature_index 1

其中 signature_index 值必须是一个对常量池的有效索引且为 CONSTANT_Utf8_info,表示类签名,方法类型签名或字段类型签名。如果当前Signature属性是类文件的属性,则这个结构表示类签名,如果当前Signature属性是方法表的属性,则表示方法类型签名,如果当前Signature属性是字段表的属性,则表示字段类型签名。

BootstrapMethods属性结构

于JDK1.7发布后添加到Class文件规范中,是一个复杂变长的属性,位于类文件的属性表中。

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 num_bootstrap_methods 1
bootstrap_method bootstrap_methods num_bootstrap_methods

其中bootstrap_method结构如下

类型 名称 数量
u2 bootstrap_method_ref 1
u2 num_bootstrap_arguments 1
u2 bootstrap_arguments num_bootstrap_arguments

特殊字符串

  • 全限定名,比如 com/yummylau/TestClass 其实就是把类全名的 “.” 换成 “/“,可以使用多个 “;”分割多个全限定名

  • 简单名称,没有类型和参数修饰的方法或者字段的名字,比如方法 inc() 和字段 m 分别标示为 inc 和 m

  • 描述符

标识字符 含义
B 基本类型 byte
C 基本类型 char
D 基本类型 double
F 基本类型 float
I 基本类型 int
J 基本类型 long
S 基本类型 short
Z 基本类型 boolean
V 基本类型 void
L 对象类型,比如 Ljava/lang/Object

针对数组,每一个维度使用一个前置的”[“字符来描述,比如定义一个 “java.lang.String[][]”数组,被记录为“[[java.lang.String;”一个整型数组 “int[]” 被记录为[I
针对方法

方法场景 描述符
void inc() ()V
java.lang.String toString() ()Ljava/lang/String;
int indexOf(char[]source,int sourceOffest,int sourceCount,char[] target,int targetOffset,int targetCOunt,int formIndex) ([CII[CIII)I