类文件(.class
文件扩展名)是包含Java字节码 ByteCode
的文件,可以在Java虚拟机上执行,每个类文件包含了一个类,接口或者模块(Java 9)的定义
Java程序(.java
文件)可以通过 Java compiler 生成字节码文件,其他基于JVM的语言也都可以通过自己的编译器生成字节码文件,例如Scala,Groovy等
JVM是与平台无关的,类文件可以在多个平台上执行,这使得相应的语言也与平台无关。
Class类文件的结构
类文件由8位字节流组成, 多字节数据使用big-endian的方式存储
类文件是按照ClassFile
结构生成的字节流,字段连续存储,没有任何填充或对齐。
Class文件格式采是用类C的结构符号表示的伪结构存储数据,有两种数据类型,无符号数(u1,u2,u4,u8)和表(*_info)
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Class 文件中涉及类/方法等名称和描述
名称
Binary Class and Interface Names
全限定名称,也叫二进制名称
例如 Thread
正常的二进制名称为 java.lang.Thread
,但是因为一些历史原因,内部格式下,.
被替换成了正斜线/
,使用CONSTANT_Utf8_info
结构(即Utf8编码的字符串常量)表示其为java/lang/Thread
Unqualified Names
非限定名称,即简单名称,例如Thread
描述符
描述符是表示字段或方法类型(type)的字符串,使用modified UTF-8(UTF-8缩略编码)
Field Descriptors
基本数据类型用一个大写字符表示,int
对应 I
对象类型使用一个L
加上全限定,末尾加上分号;
表示,Object
对应Ljava/lang/Object;
数组每个维度使用一个前置[
表示,double[][][]
对应[[[D
Method Descriptors
由零个或者多个参数描述符,加上一个返回值描述符组成
( {ParameterDescriptor} ) ReturnDescriptor
Object m(int i, double d, Thread t) {...}
对应
(IDLjava/lang/Thread;)Ljava/lang/Object;
magic & version
魔数magic number :识别类文件格式,值为0xCAFEBABE
版本号:M.m(主版本号M,副版本号 m)
major version从45开始的,每个大版本号向上加1
JDk1.1.* 支持45.0 到 45.65535
JDk1.8.* 支持45.0 到 52.65535
JDk1.k.* 支持主版本号45.0 到 44+k.0 (k>=2)
常量池
常量池数目constant_pool_count,数目等于常量表的条目数+1
常量池constant_pool是一张表,结构化的表示各种字符串常量,类和接口名称,字段名称等,表索引为1到constant_pool_count - 1
它的用处在于:Java虚拟机指令不依赖于运行时的布局,编译时没有链接这一步,所以指令引用的是constant_pool表中的符号信息
通用结构
cp_info {
u1 tag;
u1 info[];
}
表中的每一项都以一个1byte的标志位开头,指明该项的常量类型,之后是紧跟相应的内容,每种类型都有自己的结构
CONSTANT_Class_info 结构
CONSTANT_Class_info {
u1 tag;
u2 name_index;
}
tag :CONSTANT_Class (7)
name_index:是constant_pool
中有效的索引,其对应的位置必定是一个CONSTANT_Utf8_info
结构,表示类或接口的名称
CONSTANT_Utf8_info 结构
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
tag :CONSTANT_Utf8 (1)
length:长度,限定了最大长度为65535
bytes[]:Modified UTF-8形式的字符串
CONSTANT_Fieldref_info 结构
用于类加载过程中的解析
CONSTANT_Fieldref_info {
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Fieldref_info
, CONSTANT_Methodref_info
, CONSTANT_InterfaceMethodref_info
三种常量结构相似, tag
作为标识,class_index
指向相应的类或接口类型信息的常量,name_and_type_index
指向CONSTANT_NameAndType_info
结构,表示方法或字段的名称和描述符
访问标志 access_flags
类或接口的访问权限和属性的标志掩码
自身和父类的信息 this_class / super_class
u2类型,指向CONSTANT_Class_info
的索引,父类的索引可以为0(java.lang.Object
没有父类),可以获取对应的类名称
接口信息 interfaces_count / interfaces[]
描述了该类扩展的接口信息,interfaces[i]
都是constant_pool
表中的CONSTANT_Class_info
的索引, 0 ≤ i < interfaces_count
,接口的顺序按照源文件中的实现从左向右排列
字段表
fields_count
是fields
表中field_info
结构的数目
field_info
结构代表了类或接口中所有的字段(类变量class variables
(静态变量),实例变量instance variables
(类实例中包含的变量)),但是不包含他们的父类或者父接口继承来的字段
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
name_index
:指向CONSTANT_Utf8_info
结构的索引,表示一个有效的非限定名称
descriptor_index
:指向CONSTANT_Utf8_info
结构的索引,表示字段的描述符
之后是属性信息,存储一些额外的信息,例如对于final static int m = 1
,有ConstantValue
属性,包含静态变量的赋值信息。
方法表
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
同字段表类似,访问标识有部分不同
方法中的Java代码,经过编译器,编译成字节码格式之后,存放在方法的属性表内的Code
属性内
方法表中可能会有编译器自己添加的方法,例如<init>
或<clinit>
属性表
ClassFile
, field_info
, method_info
, and Code_attribute
结构中,都含有属性
Java 9共有26种预定义的属性
每个属性的名称用CONSTANT_Utf8_info
表示,属性的结构如下
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
Code属性
是可变长度的属性,包含了一个方法的JVM指令和辅助信息
native
和 abstract
类没有Code属性
//Code 属性的结构
Code_attribute {
u2 attribute_name_index; //对应一个`CONSTANT_Utf8_info `索引,内容是Code
u4 attribute_length;
u2 max_stack; //operand stack 操作数栈的最大深度
u2 max_locals;//局部变量数组的长度
u4 code_length; //长度小于65536
u1 code[code_length];//字节码指令
u2 exception_table_length;
{ u2 start_pc;
u2 end_pc; //在code数组中的起点和终点[start_pa, end_pc)
u2 handler_pc; //异常处理对应的字节码起始位置
u2 catch_type;// 是常量池中 CONSTANT_Class_info的索引,表示捕获的异常类型,为0则处理任何异常
} exception_table[exception_table_length];//异常表
u2 attributes_count;
attribute_info attributes[attributes_count];
}
Exception属性
声明所有的受查异常(checked exception,不是派生于Error
和RuntimeException
的其他所有异常)
Exceptions_attribute {
u2 attribute_name_index; //索引指向`CONSTANT_Utf8_info `表示"Exceptions"
u4 attribute_length;
u2 number_of_exceptions;
u2 exception_index_table[number_of_exceptions]; //索引指向`CONSTANT_Class_info`,表示受查异常的类型
}
LineNumberTable
出现在Code属性的attributes表中,显示code字节码和相应java源文件的行号
用于调试,以及抛出异常时,显示出错的行号
LineNumberTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 line_number_table_length;
{ u2 start_pc;
u2 line_number;
} line_number_table[line_number_table_length];
}
LocalVariableTypeTable
局部变量表,出现在Code属性的attributes表中,用来在调试过程中描述局部变量的具体类型
LocalVariableTable_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 local_variable_table_length;
{ u2 start_pc; //对应局部变量的开始位置
u2 length;//作用域[start_pc, start_pc + length)
u2 name_index;//名称
u2 signature_index;//签名信息,和描述符类似,保留了泛型类型
u2 index;//局部变量在当前栈帧局部变量表中的位置索引
} local_variable_table[local_variable_table_length];
}
SourceFile
记录源码文件的名称
Signature
Signature_attribute {
u2 attribute_name_index; // "Signature"
u4 attribute_length; //必定是2
u2 signature_index; //对应CONSTANT_Utf8_info,表示类,接口,构造器,方法,字段的签名
}
Java字节码的方法的特征签名,包含返回值和受查异常表(详细信息,参考jvms9 4.7.9.1)
[TypeParameters] ( {JavaTypeSignature} ) Result {ThrowsSignature}
InnerClass
记录内部类和宿主类之间的关联
InnerClasses_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 number_of_classes;
{ u2 inner_class_info_index;//内部类或接口信息CONSTANT_Class_info
u2 outer_class_info_index;//外部类或接口信息CONSTANT_Class_info
u2 inner_name_index;//内部类的名称CONSTANT_Class_info,匿名内部类名称为0
u2 inner_class_access_flags;
} classes[number_of_classes];
}
ConstantValue
静态变量相关的一个定长属性
ConstantValue_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 constantvalue_index;
}
javap
反编译Class文件,没有选项时输出包,类里的protected和public字段和方法
用法:javap [options] classfile
- -verbose 打印细节,可以查看常量表,栈大小,各方法的 locals 及 args 数等
- -p -private 显示所有的类和成员
- -c 输出类中各方法的 Java 字节码的指令
- -s signatures,输出内部类型签名
Reference:
Java Language and Virtual Machine Specifications
The Java® Virtual Machine Specification Java SE 9 Edition