属性表集合

在Class文件、字段表、方法表都可以携带自己的属性表集合,属性表集合不要求各个属性表具有严格顺序。

属性名称 使用位置 含义
Code 方法表 Java代码编译成的字节码指令
ConstantValue 字段表 final关键字定义的常量池
Deprecated 类,方法,字段表 被声明为deprecated的方法和字段
Exceptions 方法表 方法抛出的异常
EnclosingMethod 类文件 仅当一个类为局部类或者匿名类时才能拥有该属性,
该属性用于标识该类所在的外围方法
InnerClass 类文件 内部类列表
LineNumberTable Code属性 Java源码的行号字节码指令的对应关系
LocalVariableTable Code属性 方法的局部变量描述
StackMapTable Code属性 JDK1.6新增,供新的类型检查验证器检查和处理目标
方法的局部变量操作数有所需要的类是否匹配
Signature 类,方法表,字段表 用于支持泛型情况下的方法签名
SourceFile 类文件 记录源文件名称
SourceDebugExtension 类文件 用于存储额外的调试信息
Synthetic 类,方法表,字段表 标志方法或字段为编译器自动生成
LocalVariableTypeTable 使用特征签名代替描述符,为了引入泛型
语法之后能描述泛型参数化类型而添加
RuntimeVisibleAnnotations 类,方法表,字段表 动态注解提供支持
RuntimeInvisibleAnnotations 表,方法表,字段表 用于指明哪些注解是运行时不可见
RuntimeVisibleParameterAnnotation 方法表 作用与RuntimeVisibleAnnotations属性类似,
作用对象方法
RuntimeInvisibleParameterAnnotation 方法表 作用与RuntimeInvisibleAnnotations属性类似,
作用对象方法参数
AnnotationDefault 方法表 用于记录注解类元素的默认值
BootstrapMethods 类文件 用于保存invokeddynamic指令引用的引导方法限定符

每个属性名称都需要从常量池中引用一个CONSTANT_Utf8_info类型的常量来表示,属性值的结构完全自定义的,只需要通过一个u4长度属性去说明属性值所占用的位数即可。

类型 名称 数量
u2 attribute_name_index 1
u2 attribute_length 1
u1 info attribute_length

Code属性

Java方法体中代码经过Javac编译器处理后,最终变为字节码指令存储在Code属性内。Code属性出现在方法表属性集合中,接口抽象类中的抽象方法不存在Code属性

Code属性是Class文件中最重要的一个属性,如果把一个Java程序中的信息分为代码(方法体中的代码)元数据(类、字段、方法定义及其他信息),那整个Class文件中,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_length
u2 exception_table_length 1
exception_info exception_table exception_length
u2 attributes_count 1
attribute_info attributes attributes_count

attribute_name_index是指向CONSTANT_Utf8_info型常量的索引,其值固定为Code,代表该属性的属性名称attribute_length表示属性值长度属性名称索引与属性长度共6字节

max_stack代表操作数栈最大深度,虚拟机运行时需要根据该值来分配栈帧中的操作数栈深度

max_locals代表局部变量表所需内存空间,单位Slot,是虚拟机为局部变量表分配内存所使用的最小单位byte、char、float、int、short、boolean、returnAddress等长度不超过32位的数据类型,每个局部变量占用1个Slot,而doublelong这两种64位的数据类型则占用2个Slot。方法参数包括实例方法中的隐藏参数this显式异常处理器的参数就是try-catch语句中catch块所定义的异常方法体中定义的局部变量,都需要使用局部变量表来存放。局部变量中的Slot可以重用,当代码执行超出一个局部变量的作用域时,这个局部变量所占的Slot可以被其他局部变量所使用,Javac编译器会根据变量的作用域来分配Slot给各个变量使用,然后计算出max_locals的大小。

code_lengthcode用来存储Java源程序编译后生成的字节码指令code_length代表字节码长度code用于存储字节码指令的一系列字节流。每个字节码指令都是u1类型的数据,当虚拟机读取code中的字节码时,能够对应找出这个字节码代表的指令,指令后面是否需要跟随参数,参数应该如何理解。u1数据类型取值范围为0x00~0xFF,一共可表达256条指令,目前虚拟机规范定义了约200条编码值对应的指令含义

code_length虽然是一个u4类型的数据,但虚拟机限制方法不允许超过65535条字节码指令,即它实际上只使用了u2的长度,如果超过限制Javac编译器会拒绝编译。在编译一个很浮渣的JSP文件时,某些JSP编译器会把JSP内容和页面输出信息归并于一个方法,可能因方法生成字节码超长导致编译失败。

1
2
3
4
5
6
7
8
9
10
11
12
public com.coms.jvm.ClassFileConstantPool.TestClassA();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/coms/jvm/ClassFileConstantPool/TestClassA;

从上面的代码可以看到,实例方法明显没有参数,但localsargs_size的值却为1,这是Java程序一个很重要的访问机制,在任何实例方法中,都可以通过this关键字访问到此方法的所属的对象,Javac编译器编译时把对this关键字的访问转变为对一个普通方法参数的访问,在虚拟机调用实例方法时自动传入此参数。

在字节码指令之后是方法的显示异常处理表集合exception_table,它对应Code属性来说并不是必须存在的。

1
2
3
4
5
6
Exception table:
from to target type
0 4 8 Class java/lang/Exception
0 4 17 any
8 13 17 any
17 19 17 any

Exceptions属性

Exceptions属性是在方法表中于Code属性平级的属性,区别于前面的Code属性中的异常表。其作用是列举方法中可能抛出的受检查的异常,也就是方法描述时在throws关键字后面列举的异常

1
2
Exceptions:
throws java.lang.Exception, java.sql.SQLException
类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 attribute_of_exceptions 1
u2 exception_index_table number_of_exceptions

attribute_of_exceptions表示方法可能抛出attribute_of_exceptions种异常,每一种受查异常使用一个exception_index_table项表示,exception_index_table时一个指向常量池种CONSTANT_Class_info型常量索引,代表了该受查异常的类型

LineNumberTable属性

LineNumberTable属性用于描述Java源码行号字节码行号(字节码偏移量)之间的对应关系。并非运行时必需属性,默认会生成到Class文件中,可以在Javac种使用-g:none-g:line选项来取消要求生成该项信息。如果选择不生成该属性,当程序抛出异常时,堆栈中将不会显示出错的行号,在调试程序时也无法按照源码行来设置断点

类型 名称 数量
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_table是一个数量为line_number_table_length、类型为line_number_info的集合,line_number_info包括了字节码行号start_pcJava源码行号line_number两个u2类型的数据。

LocalVeriableTable属性

LocalVeriableTable属性用于描述栈帧中局部变量表中的变量Java源码中定义的变量之间的关系,非运行时必需属性,默认生成到Class文件中,可以在Javac种使用-g:none-g:vars选项来取消要求生成该项信息。如果不生成该属性,当其他人引用该方法时,所有参数名称都将丢失,IDE将会使用诸如arg0arg1之类的占位符代替原有的参数名,虽对程序运行无影响,但对代码编写带来较大不便,且在调试时无法根据参数名称从上下文中获得参数值

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 local_veriable_table_length 1
local_veriable_info local_veriable_table local_veriable_table_length

local_veriable_info代表一个栈帧源码中局部变量的关联。下表是local_veriable_info项目结构。

类型 名称 数量
u2 start_pc 1
u2 length 1
u2 name_index 1
u2 descriptor_index 1
u2 index 1

start_pclength属性分别代表这个局部变量生命周期开始字节码偏移量及其作用范围覆盖长度,两者结合就是该局部变量在字节码之中的作用范围

name_indexdescriptor_index都是指向常量池中CONSTANT_UTF8_info型常量索引,分别代表局部变量名称以及该局部变量的描述符

index是该局部变量在栈帧局部变量表中Slot位置,当该变量数据类型是64位类型是,它占用的Slotindexindex+1

LocalVeriableTypeTable属性

LocalVeriableTypeTable属性是LocalVeriableTable属性的姐妹属性,其结构与LocalVeriableTable非常相似,仅仅把记录字段描述符descriptor_index替换成了字段的特征签名,对于非泛型类型来说,描述符特征签名能描述的信息基本上一致,但泛型中,描述符中泛型的参数化类型被擦除,描述符不能准确描述泛型类型。

SourceFile属性

SourceFile属性用于记录生成这个Class文件的源码文件名称,定长属性,非运行时必需属性,默认生成到Class文件中,可以在Javac种使用-g:none-g:source选项来取消要求生成该项信息。Java中大多数类的类名和文件名一致,但内部类列外,如果不生成该属性,当抛异常时堆栈中将不会显示出错误代码所属的文件名

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

sourcefile_index数据项是指向常量池中CONSTANT_UTF8_info型常量索引,常量值是源文件的文件名

ConstantValue属性

ConstantValue属性的作用是通知虚拟机自动为静态变量赋值,定长属性,只有被final关键字修饰的变量才可以使用这项属性。对于static类型的变量赋值时在实例构造器<init>方法中进行的,而static类型变量,有两种方式可以选择,在类构造器<clinit>方法中或者使用ConstantValue属性。目前在Sun Javac编译器中,如果同时使用finalstatic修饰变量,且该变量数据类型是基本数据类型或者java.lang.String,就生成ConstantValue属性类进行初始化,如果这个变量没有别final修饰或者并非基本类型及字符串,则会<clinit>方法中进行初始化

虚拟机规范中只要求了有ConstantValue属性的字段必须设置ACC_STATIC标志,并没有强制要求设置ACC_FINAL标志,对final关键字的要求是Javac编译器自己加入的限制。

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

constantvalue_index数据项代表了常量池中一个字面量常量的引用,根据字段类型不同,字面量可以是CONSTANT_Long_info、CONSTANT_Float_info、CONSTANT_Double_info、CONSTANT_Integer_info、CONSTANT_String_info

InnerClasses属性

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代表需要记录多少个内部类信息,每个内部类信息都由一个inner_classes_info表进行描述。

类型 名称 数量
u2 inner_class_info_index 1
u2 other_class_info_index 1
u2 inner_name_index 1
u2 inner_class_access_flags 1

inner_class_info_indexother_class_info_index都是指向常量池中CONSTANT_Class_info型常量索引,分别代表内部类宿主类符号引用inner_name_index是指向常量池中CONSTANT_UTF8_info型常量索引,代表这个内部类的名称,如果是匿名内部类该项值为0inner_class_access_flags内部类的访问标志,类似于类的access_flags

Deprecated & Synthetic属性

DeprecatedSynthetic两个属性都属于标志类型布尔属性,只存在有和没有的区别,没有属性值的概念。

Deprecated属性用于表示某个类、字段、方法,已经被程序作者定为不再推荐使用,通过在代码中使用@deprecated注解进行设置。

Synthetic属性代表此字段或者方法并不是由Java源代码直接生成的,而是由编译器自行添加的,标识一个类、字段或者方法是编译器自动产生的,也可以设置访问标志中的ACC_SYNTHETIC标志位。所有由非用户代码产生的类、方法及字段都应当至少设置Synthetic属性ACC_SYNTHETIC标志位中的一项,唯一例外实例构造器<init>类构造器<clinit>

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

attribute_length的值必须为0x00000000,因为没有任何属性值需要设置。

StackMapTable属性

StackMapTable属性JDK1.6增加到Class文件规范中,在JDK1.7强制代替原本基于类型推断字节码验证器,它是一个复杂的变长属性,位于Code属性的attributes属性表中,该属性会在虚拟机类加载的字节码验证阶段被新类型检查验证器使用,目的在于替代以前比较消耗性能基于数据流分析类型推导验证器

新验证器在同样能保证Class文件合法性的前提下省略了在运行期通过数据流分析去确认字节码的行为逻辑合法性的步骤,而是在编译阶段将一系列的验证类型直接记录在Class文件中,通过检查这些验证类型代替了类型推导过程,从而大幅度提升了字节码验证的性能。

StackMapTable属性中包含0至多个栈映射帧每个栈映射帧显式隐式地代表了一个字节码偏移量,用于表示该执行到该字节码时局部变量表操作数栈的验证类型类型检查验证器会通过检查目标方法局部变量操作数栈所需要的类型来确定一段字节码指令是否符合逻辑约束

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

版本号大于或等于50.0Class文件中,如果方法的Code属性中没有附带StackMapTable属性,意味着它带有一个隐式的StackMap属性,起作用等同于number_of_entries值为0StackMapTable属性。一个方法的Code属性最多只能有一个StackMapTable属性,否则将抛出ClassFormatError异常

Signature属性

Signature属性JDK1.5增加到Class文件规范中,可选定长属性,可以出现于类、字段表和方法表结构的属性表中。任何类、接口、初始化方法或成员的泛型签名如果包含了类型变量参数化类型,则Signature属性会为它记录泛型签名信息,之所以这样是由于Java的泛型采用的是擦除法实现的伪泛型,在字节码中,泛型信息编译之后类型变量参数化类型被擦除了,运行期做反射时无法获得泛型信息。Signature属性就是为了弥补这个缺陷,现在Java的反射API能获取泛型类型,最终的数据类来源Signature属性

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

signature_index值必须时一个对常量池的有效索引,参量池在该索引处必须是CONSTANT_UTF8_info结构,表示类签名、方法类型签名或字段类型签名。Signature属性类文件属性该结构表示类签名Signature属性方法表的属性该结构表示方法类型签名Signature属性字段表的属性该结构表示字段类型签名

BootstrapMethods属性

BootstrapMethods属性在JDK1.7增加到Class文件规范中,复杂变长属性,位于类文件属性表中。该属性用于保存invokedynamic指令引用的引导方法限定符。如果某个类文件结构的常量池中曾经出现过CONSTANT_InvokeDynamic_info类型的常量,那该类文件的属性表中必须存在一个明确的BootstrapMethods属性即使出现多次也最多也只能有一个

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

num_bootstrap_methods值为bootstrap_methods[]数组中的引导方法限定符的数量bootstrap_methods[]数组的每个成员都包含一个指向常量池CONSTANT_MethodHandle结构的索引值,它代表示一个引导方法,还包含了该引导方法静态参数的序列。其中num_bootstrap_method表结构如下所示:

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

bootstrap_method_ref值必须是一个对常量池的有效索引,且该索引值必须是一个CONSTANT_MethodHandle_info结构;num_bootstrap_arguments表示bootstrap_arguments[]数组成员的数量;bootstrap_arguments[]数组成员必须是一个对常量池有效的索引,且必须是一下结构之一:CONSTANT_String_info、CONSTANT_Class_info、CONSTANT_Integer_info、CONSTANT_Long_info、CONSTANT_Float_info、CONSTANT_Double_info、CONSTANT_MethodHandle_info、CONSTANT_MothodType_info