Class文件分析

139 阅读6分钟

1 简述

class文件是JVM虚拟机能够识别的文件格式,目前Java、kotlin等语言都会编译成该格式文件,让JVM能够加载运行。

2 格式

在字节码文件中只存储了两种类型的数据:

  • 无符号数u1(u2、u3、u4...),表示1个字节(2个字节、3个、4个...),
  • 表(无符号的集合)。 | 类型 | 名称 | 数量 | 数量 | | --- | --- | --- | --- | | u4 | magic | 1 | | | u2 | minor_version | 1 | | | u2 | major_version | 1 | | | u2 | constant_pool_count | 1 | | | cp_info | constant_pool | constant_pool_count-1 | | | 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 | attributes_count | 1 | | | attribute_info | attributes | attributes_count | |

3 具体内容

magic

魔数,CAFEBABE,标记文件类型为Class文件

minor_version

次版本号

major_version

主版本号

constant_pool_count

常量池中常量的个数,有这个数,JVM才知道申请多少空间。
从1开发,将第0项常量空了出来,这是为了满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义,这种情况可用索引值0来表示。

constant_pool

常量池,用于存放其结构及其子结构中引用的各种字符串常量、类和接口名称、字段名称和其他常量。
constant_pool表的索引从 1 到constant_pool_count- 1。

常量池中数据项类型类型标志类型描述
CONSTANT_Utf81UTF-8编码的Unicode字符串
CONSTANT_Integer3int类型字面值
CONSTANT_Float4float类型字面值
CONSTANT_Long5long类型字面值
CONSTANT_Double6double类型字面值
CONSTANT_Class7对一个类或接口的符号引用
CONSTANT_String8String类型字面值
CONSTANT_Fieldref9对一个字段的符号引用
CONSTANT_Methodref10对一个类中声明的方法的符号引用
CONSTANT_InterfaceMethodref11对一个接口中声明的方法的符号引用
CONSTANT_NameAndType12对一个字段或方法的部分符号引用
CONSTANT_MethodHandle15JAVA SE 7
CONSTANT_MethodType16JAVA SE 7
CONSTANT_Dynamic17JAVA SE 11
CONSTANT_InvokeDynamic18JAVA SE 7
CONSTANT_Module19JAVA SE 9
CONSTANT_Package20JAVA SE 9

常量池主要存放两大类常量:

  • 字面量
  • 符号引用

字面量

文本字符串、声明为final的常量值、基本数据类型等

符号引用

  1. 类和接口的全限定名
  2. 字段的名称和描述符
  3. 方法的名称和描述符

全限定名:a/b/c/D 这个就是类的全限定名,仅仅是把包名的“.“替换成”/”,为了使连续的多个全限定名之间不产生混淆,在使用时最后一般会加入一个“;”表示全限定名结束。

简单名称:是指没有类型和参数修饰的方法或者字段名称,上面例子中的类的add()方法和num字段的简单名称分别是add和num

描述符的作用是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。

标志符含义
B基本数据类型byte
C基本数据类型char
D基本数据类型double
F基本数据类型float
I基本数据类型int
J基本数据类型long
S基本数据类型short
Z基本数据类型boolean
V代表void类型
L对象类型,比如:Ljava/lang/Object;
[数组类型

Java在没有加载进JVM时,是没有分配内存空间的,也就是我们的方法里如果new一个类,class文件无法确定对象的地址,这个时候需要用符号引用去标识,new 的是哪个类。
这样运行的时候才能去new到正确的类,将符号引用转为直接地址引用。

access_flags

class文件访问标识信息

标志名标志值标志含义针对的对像
ACC_PUBLIC0x0001public类型所有类型
ACC_FINAL0x0010final类型
ACC_SUPER0x0020使用新的invokespecial语义类和接口
ACC_INTERFACE0x0200接口类型接口
ACC_ABSTRACT0x0400抽象类型类和接口
ACC_SYNTHETIC0x1000该类不由用户代码生成所有类型
ACC_ANNOTATION0x2000注解类型注解
ACC_ENUM0x4000枚举类型枚举
ACC_MODULE0x8000模块类型模块

this_class

类索引,用于确定该类的全限定名。

super_class

确定父类全限定名的索引,除了Object类的为0,其他类都有父类索引。
类索引和父类索引各自指向一个类型为CONSTANT_Class_info类型的常量,通过CONSTANT_Class_info类型的常量中的索引值可以找到定义在CONSTATN_Utf8_info类型的常量中的全限定名字符串。

interfaces_count

当前类直接实现的接口的数量或当前接口直接继承的接口的数量。

interfaces

对当前类或当前接口直接实现或继承的所有接口的描述。

fields_count

fields_count描述字段表的个数。

fields

字段表用于描述接口或者类中声明的变量。字段(field)包括类级变量以及实例级变量,但是不包括方法内部、代码块内部声明的局部变量。

field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

methods_count

methods中method_info的数目。

methods

method_info {
    u2             access_flags; // 访问标志 public private protected static
    u2             name_index; // 方法名索引
    u2             descriptor_index; // 描述符索引
    u2             attributes_count; // 属性计数器
    attribute_info attributes[attributes_count]; // 属性集合
}

attributes_count

属性表长度

attributes

方法表集合之后的属性表集合,指的是class文件所携带的辅助信息,比如该class文件的源文件的名称。以及任何带有RetentionPolicy.CLASS 或者RetentionPolicy.RUNTIME的注解。这类信息通常被用于Java虚拟机的验证和运行,以及Java程序的调试,一般无须深入了解。
此外,字段表、方法表都可以有自己的属性表。用于描述某些场景专有的信息。

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

更多信息:docs.oracle.com/javase/spec…