Class类文件结构

任何一个Class文件都对应着唯一一个类或接口的定义信息。

Class文件是一组以8位字节为基础单位的二进制流,当遇到需要占用8位字节以上空间的数据项时,会按照高位在前的方式分割成若干个8为字节进行存储。

存储格式:

无符号数和表

无符号数属于基本的数据类型,u1、u2、u4、u8分别表示1、2、4、8个字节的无符号数,可以用来描述数字、索引引用、数量值、或者按照UTF-8编码构成字符串值。

表是多个无符号数或者其他表作为数据项构成的复合数据类型,习惯性的以“_info”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一个表。

Class文件格式

名称 描述 类型 数量
magic 魔数 u4 1
minor_version 次版本号 u2 1
major_version 主版本号 u2 1
constant_pool_count 常量池容量计数值 u2 1
constant_pool 常量池 cp_info constant_pool_count-1
access_flags 访问标志 u2 1
this_class 类索引 u2 1
` super_class` 父类索引 u2 1
interfaces_count 接口数量 u2 1
interfaces 接口 u2 interfaces_count
fields_count 属性数量 u2 1
fields 属性 field_info fields_count
methods_count 方法数量 u2 1
methods 方法 method_info methods_count
attributes_count 附加属性数量 u2 1
attributes 附加属性 attribute_info attributes_count

示例代码:

package com.jvm.test.demo.clazz;

public class TestClass {

    private int m;

    private int inc() {
        return m + 1;
    }
}

javac编译后的class文件

Class类文件结构_第1张图片

使用javap -verbose查看class文件

Class类文件结构_第2张图片

魔数

Class文件头四个字节。

字节码 描述
cafe babe 确定这个class文件是否为一个能被虚拟机接受的class是文件

Class文件版本

版本:第5和第6字节0000表示次版本号,第7和第8字节0034表示主版本号。对应jdk的版本。

字节码 描述
0000 此版本号0
0034 主版本号52

高版本的JDK能向下兼容以前版本的class文件,但不能运行以后版本的class文件,即使文件格式并未发生任何变化,虚拟机也必须拒绝执行超过其版本号的class文件。

常量池

字面量:如文本字符串、声明为final的常量值等。

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

常量池中数量不固定,需有constant_pool_count代表数量。从1开始,因为为了满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义,这种情况下把索引值置为0标识
0x0013-->十进制为19,表示有18个常量。

常量池中每一项都是一个表。表开始第一位是一个u1类型的标志位(tag)代表属于哪个类型。每个类型有各自的结构。

类型 标志 描述
CONSTANT_Utf8_info 1 UTF-8编码的字符串
CONSTANT_Integer_info 3 整型字面量
CONSTANT_Float_info 4 浮点型字面量
CONSTANT_Long_info 5 长整型字面量
CONSTANT_Double_info 6 双精度浮点型字面量
CONSTANT_Class_info 7 类或接口的符号引用
CONSTANT_String_info 8 字符串类型字面量
CONSTANT_Fieldref_info 9 字段的符号引用
CONSTANT_Methodref_info 10 类中方法的符号引用
CONSTANT_InterfaceMethodref_info 11 接口中方法的符号引用
CONSTANT_NameAndType_info 12 字段或方法的部分符号引用
CONSTANT_MethodHandle_info 15 表示方法句柄
CONSTANT_MethodType_info 16 标识方法类型
CONSTANT_Dynamic_info 17 表示一个动态计算常量
CONSTANT_InvokeDynamic_info 18 表示一个动态方法调用点
CONSTANT_Module_info 19 表示一个模块
CONSTANT_Package_info 20 表示一个模块中开放或者导出的包


字节码 类型 描述
0a 0004 000f CONSTANT_Methodref_info Object中init方法相关
09 0003 0010 CONSTANT_Fieldref_info 字段m相关
07 0011 CONSTANT_Class_info 类相关
07 0012 CONSTANT_Class_info 父类相关
01 0001 6d CONSTANT_Utf8_info 6d对应ascII中的m
01 0001 49 CONSTANT_Utf8_info 49对应ascII中的I
01 0006 3c696e69743e CONSTANT_Utf8_info 对应的ascII数据为
01 0003 282956 CONSTANT_Utf8_info 对应的ascII数据为()V
01 0004 436f6465 CONSTANT_Utf8_info 对应的ascII数据为Code
01 000f 4c696e654e756d6265725461626c65 CONSTANT_Utf8_info 对应的ascII数据为LineNumberTable
01 0003 696e63 CONSTANT_Utf8_info 对应的ascII数据为inc
01 0003 28 2949 CONSTANT_Utf8_info 对应的ascII数据为()I
01 000a 536f7572636546696c65 CONSTANT_Utf8_info 对应的ascII数据为SourceFile
01 000e 54657374436c6173732e6a617661 CONSTANT_Utf8_info 对应的ascII数据为TestClass.java
0c 0007 0008 CONSTANT_NameAndType_info 方法和字段的相关
0c 0005 0006 CONSTANT_NameAndType_info 方法和字段的相关
01 0021 636f6d2f6a766d2f746573742f64656d6f2f636c617a7a2f54657374436c617373 CONSTANT_Utf8_info com/jvm/test/demo/clazz/TestClass
01 0010 6a6176612f6c616e672f4f626a656374 CONSTANT_Utf8_info java/lang/Object

Class文件中方法、字段等都需要引用CONSTANT_Utf8_info型常量来描述名称,所以CONSTANT_Utf8_info型常量最大长度就是Java中方法、字段名的最大长度,u2能表达的最大值65535

访问标志

两个字节,用于识别一些类或者接口层次的访问信息。包括这个Class是类还是接口,是否被定义为public类型,是否定义为abstract类型等。


字节码 描述
0021 为public所以最后一位为真0001,其余为为假。且1.2之后第二位真

类索引、父类索引、接口索引集合

使用这三项数据确定类的继承关系。
类索引:用于确定这个类的全限定名
父类索引:用于确定这个类的父类的全限定名。除了Object外,其余父类索引都不为0
接口索引集合:描述类实现了哪些接口,从左到右排列。

字节码 描述
0003 类索引,指向索引号为3的类描述常量,从而找到类的全限定名字符串
0004 父类引,指向索引号为4的类描述常量,从而找到类的全限定名字符串
0000 标识接口数量为0,没有实现接口,不再占任何字节

字段表集合

用于描述接口或者类中声明的变量。包括类变量以及实例变量,不包括在方法内部声明的局部变量。

字段表结构

类型 名称 数量 描述
u2 access_flags 1 字段修饰符
u2 name_index 1 名称索引
u2 descriptor_index 1 描述符索引
u2 attributes_count 1 属性数量
attribute_info attributes attributes_count 属性表集合

简单名称:没有类型和参数修饰的方法或者字段名称
全限定名:类的全路径,例如com/jvm/test/demo/clazz/TestClass;
描述符:描述字段的数据类型、方法的参数列表(包括数量、类型、顺序)和返回值


Class类文件结构_第3张图片

字段访问标志

标志 描述
ACC_PUBLIC 0x0001 public 访问修饰符
ACC_PRIVATE 0x0002 private 访问修饰符
ACC_PROTECTED 0x0004 protected 访问修饰符
ACC_STATIC 0x0008 static
ACC_FINAL 0x0010 final
ACC_VOLATILE 0x0040 valatile
ACC_TRANSIENT 0x0080 transient
ACC_SYNTHETIC 0x1000 synthetic
ACC_ENUM 0x4000 enum
字节码 描述
0001 表示1个字段数据
0002 标识访问标志,为private
0005 字段的简单名称引用,到索引5
0006 引用到索引6,描述符I标识返回值为int类型
0000 标识没有额外信息存储

数组类型,每一位度前置一个”[“字符描述。例如java.lang.String[][]--->[[Ljava/lang/Strang

方法表集合

描述方法信息

方法表结构

类型 名称 数量 描述
u2 access_flags 1 名称修饰符
u2 name_index 1 名称索引
u2 descriptor_index 1 描述符索引
u2 attributes_count 1 属性数量
attribute_info attributes attributes_count 属性表集合

方法访问标志:

Class类文件结构_第4张图片

方法里的Java代码,经过编译器编译成字节码指令后,存放在方法属性表集合”Code“中。

字节码 描述
0002 表示2个方法
0001 标识访问标志,为public
0007 索引7,方法名为
0008 索引8,返回值为()V
0001 表示属性表集合有一属性
0009 属性名称索引为9,对应Code属性

属性表集合

Class文件、字段表、方法表都可以携带自己的属性表,用于描述场景专有信息。

属性表结构

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

Code属性

类型 名称 数量 描述
u2 attribute_name_index 1 attribute_name_index是一项指向CONSTANT_Utf8_info型常量的索引,常量值固定为Code,它代表了该属性的属性名称
u4 attribute_length 1 attribute_length指示了属性值的长度
u2 max_stack 1 max_stack代表了操作数栈(Operand Stacks)深度的最大值。虚拟机运行的时候需要根据这个值来分配栈帧(StackFrame)中的操作栈深度。
u2 max_locals 1 max_locals代表了局部变量表所需的存储空间,单位为Slot(虚拟机为局部变量分配内存所使用的最小单位)
u4 code_length 1 code_length代表字节码长度;理论上一个方法的字节码不超过u4,但实际是u2,如果超过这个限制,javac会拒绝
u1 code code_length code是用于存储字节码指令的一系列字节流
u2 exception_table_length 1 异常表长度
exception_info exception_table exception_table_length
u2 attributes_count 1 属性数量
attribute_info attributes attributes_count
字节码 描述
0001 001d 属性值长度为29
0001 最大栈深度为1
0001 最大局部变量空间为1
0000 00005 标识5个字节的指令
2a b7 00 01 b1 指令数据,方法执行指令操作步骤
0000 异常表长度为0,无异常信息
0001 标识有额外属性1个
000a 额外属性索引为10,标识LineNumberTable属性

局部变量locals为1,因为任何实例方法里面,都可以通过”this“关键字访问到此方法所属的对象。Javac编译器编译时把对this关键字的访问转变为对一个普通方法参数的访问,在虚拟机调用实例方法时自动传入此参数。实例方法的局部变量表中至少存在一个执行当前对象实例的局部变量,局部变量表也会预留第一个Slot来存放对象实例的引用,方法参数从1开始计算。如果声明为static,那就是0。

Exceptions属性

作用是例举出方法中可能抛出的受检查异常,也就是方法描述时在throws后的异常。

类型 名称 数量 描述
u2 attribute_name_index 1 名称索引
u4 attribute_length 1 长度
u2 number_of_exceptions 1 异常种类数量
u2 exception_index_table number_of_exceptions 执行常量池CONSTANT_Class_info的索引,代表受查异常类型

LineNumberTable属性

描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系。

类型 名称 数量 描述
u2 attribute_name_index 1 名称索引
u4 attribute_length 1 长度
u2 line_number_table 1 数量
line_number_info line_number_table line_number_table 包括了start_pc标识字节码行号,line_number标识Java源码行号。都是u2类型数据项
字节码 描述
0000 0006 属性值长度为6
0001 有1个行号对比
0000 0003 表示源码和字节码行号比对
0002 表示私有方法,inc
000b 名称索引,方法名称为inc
000c 描述符索引
0001 表示有1个属性集合
0009 属性索引,属性名称为Code
0000 001f 属性长度
0002 最大栈为2
0001 局部变量为1
0000 0007 字节码长度为7个
2ab4 0002 0460 ac 字节码指令
0000 无异常
0001 有一个额外属性
000a 名称索引,属性名称LineNumberTable
0000 0006 属性长度为6
0001 数量为1
0000 0008 行号对比

LocalVariableTable属性

描述栈帧中局部变量表中的表量与Java源码中定义的变量之间的关系。

类型 名称 数量 描述
u2 attribute_name_index 1 名称索引
u4 attribute_length 1 长度
u2 local_variable_table_length 1 数量
local_variable_info local_variable_table local_variable_table_length

local_variable_info

类型 名称 数量 描述
u2 start_pc 1 局部变量生命周期开始的字节码偏移量
u2 length 1 局部变量作用范围的长度
u2 name_index 1 指向常量池utf8常量的索引
u2 descriptor_index 1 局部变量的描述符
u2 index 1 局部编码在栈帧局部变量表中Slot的位置,数据类型为64位时,占用index以及index+1

SourceFile属性

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

类型 名称 数量 描述
u2 attribute_name_index 1 名称索引
u4 attribute_length 1 长度
u2 sorucefile_index 1 指向utf8常量的索引,值为源文件的名称
字节码 描述
0001 表示有1个属性
000d 索引13,属性名称SourceFile
0000 0002 两个字节长度
000e 名称索引TestClass.java

ConstantValue属性

通知虚拟机自动为静态变量赋值,只有被static关键字修饰的变量(类变量)才可以使用这项属性。
对于非static类型的变量(也就是实例变量)的赋值是在实力构造器方法进行的;对于类变量,可以选择在类构造器方法或使用ConstantValue属性。如果同时使用final和static修饰,且变量为基本数据类型或者String,就生成ConstantValue来进行初始化。否则,在中进行初始化。

类型 名称 数量 描述
u2 attribute_name_index 1 名称索引
u4 attribute_length 1 固定为2
u2 constantvalue_index 1 标识常量池中一个字面量常量的引用

innerClasses属性

用于记录内部类与宿主类之间的关联。

类型 名称 数量 描述
u2 attribute_name_index 1 名称索引
u4 attribute_length 1 固定为2
u2 number_of_classes 1 内部类数量
inner_classes_info inner_classes number_of_classes 内部类信息

inner_classes_info

类型 名称 数量 描述
u2 inner_class_info_index 1 内部类的符号引用
u2 outer_class_info_index 1 宿主类的符号引用
u2 inner_name_index 1 内部类的名称索引,如果是匿名内部类,值为0
u2 inner_class_access_flags 1 内部类的访问标志

inner_class_access_flags

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

Deprecated及Synthetic属性

都是布尔属性。
Deprecated表示有@deprecated注解进行设置的代码
Synthetic属性和ACC_SYNTHETIC,非用户代码产生的类、方法、字段至少设置一个。实例构造器和类构造器方法除外。

类型 名称 数量 描述
u2 attribute_name_index 1 名称索引
u4 attribute_length 1 长度

StackMapTable属性

位于Code属性的属性表中。会在虚拟机类加载的字节码验证阶段被新类型检查验证器使用,目的在于代替以前比较消耗性能的基于数据流分析的类型推导验证器。

包含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 栈帧信息

一个方法的Code属性之多只能有一个StackMapTable属性,否则会抛出ClassFormatError异常。

Signature属性

可出现在类、字段表和方发表结构的属性表中。任何类、接口、初始化方法或成员的泛型签名如果包含了类型变量或参数化类型,Signature属性会记录泛型签名信息。JavaAPI能够获取泛型类型,数据来源也是这个属性。

类型 名称 数量 描述
u2 attribute_name_index 1 名称索引
u4 attribute_length 1 长度
u2 signature 1 属性信息

BootstrapMethods属性

位于类文件的属性表中。用于保存invokedynamic指令引用的引导方法限定符。类文件的属性表中最多有一个BootstrapMethods属性

类型 名称 数量 描述
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 信息

MethodParameters属性

JDK8以后加入Class文件格式中。用来记录方法的各个形参名称和信息。

类型 名称 数量 描述
u2 attribute_name_index 1 名称索引
u4 attribute_length 1 长度
u1 parameters_count 1 数量
parameter parameters parameters_count 信息

parameter

类型 名称 数量 描述
u2 name_index 1 参数的名称
u2 access_flags 1 参数的状态指示器

access_flags:以下一种或多种

0x0010(ACC_Final):表示该参数被final修饰
0x1000(ACC_SYNTHETIC):表示该参数并未出现在源文件,是编译器自动生成
0x8000(ACC_MANDATED):表示该参数是源文件中隐式定义的。

你可能感兴趣的:(Class类文件结构)