字节码文件
字节码文件中包含的内容有包含类的版本信息、字段、方法以及接口等描述信息外,还有常量池表(Constant Pool Table),包括各种字面量和对类型、域(Field)和方法的符号引用,指令代码行号表等信息
- java栈中:局部变量表、操作数栈
- java堆中
- 常量池
- 帧数据区、方法区的剩余情况
文件结构
u1、u2、u4、u8分别表示1个字节、2个字节、4个字节、8个字节的无符号数
1 | ClassFile { |
魔数
class文件的第1-4个字节表示该文件的魔数,魔数是用来识别class文件格式,java的class文件中都是 0xCAFEBABE
副版本号
class文件的第5-6个字节表示该class文件的副版本号,即编译该class文件的JDK副版本号
主版本号
class文件的第7-8个字节表示该class文件的主版本号,即编译该class文件的JDK主版本号
常量池
常量池分为常量池计数器和常量池表
常量池计数器
由于常量池列表中的常量数并不固定,所以需要一个常量池计数器来统计常量池中常量项的数量,计数从1开始,记录常量池表中的数量,减一等于常量池表的长度
常量池表
常量池表中主要存放字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放
字面量主要是文本字符串和声明为final的常量值
1 | cp_info { |
tag项说明
常量类型 | 标志值 | 描述 | 结构 |
---|---|---|---|
CONSTANT_Class |
7 | 类或接口的符号引用 | { u1 tag; u2 name_index; } |
CONSTANT_Fieldref |
9 | 字段的符号引用 | { u1 tag; u2 class_index; u2 name_and_type_index; } |
CONSTANT_Methodref |
10 | 类中方法的符号引用 | { u1 tag; u2 class_index; u2 name_and_type_index; } |
CONSTANT_InterfaceMethodref |
11 | 接口中方法的符号引用 | { u1 tag; u2 class_index; u2 name_and_type_index; } |
CONSTANT_String |
8 | 字符串类型字面量 | { u1 tag; u2 string_index; } |
CONSTANT_Integer |
3 | 整型字面量 | { u1 tag; u4 bytes; } |
CONSTANT_Float |
4 | 浮点型字面量 | { u1 tag; u4 bytes; } |
CONSTANT_Long |
5 | 长整型字面量 | { u1 tag; u4 high_bytes; u4 low_bytes; } |
CONSTANT_Double |
6 | 双精度浮点型字面量 | { u1 tag; u4 high_bytes; u4 low_bytes; } |
CONSTANT_NameAndType |
12 | 字段或方法的符号引用 | { u1 tag; u2 name_index; u2 descriptor_index; } |
CONSTANT_Utf8 |
1 | UTF-8编码的字符串 | { u1 tag; u2 length; u1 bytes[length]; } |
CONSTANT_MethodHandle |
15 | 方法句柄 | { u1 tag; u1 reference_kind; u2 reference_index; } |
CONSTANT_MethodType |
16 | 标志方法类型 | { u1 tag; u2 descriptor_index; } |
CONSTANT_InvokeDynamic |
18 | 动态方法调用点 | { u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index; } |
符号引用主要是类和接口的全限定名,字段的名称和描述符,方法的名称和描述符
当虚拟机做类加载时,将会从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址中
字段描述符
FieldType term | Type |
---|---|
B |
byte |
C |
char |
D |
double |
F |
float |
I |
int |
J |
long |
L ClassName ; |
对象reference` |
S |
short |
Z |
boolean |
[ |
数组reference |
访问标识
类访问标识
标识名称 | 值 | 含义 |
---|---|---|
ACC_PUBLIC |
0x0001 | 标识为public,可以被包外访问 |
ACC_FINAL |
0x0010 | 标识被声明为final,不允许有子类 |
ACC_SUPER |
0x0020 | 表示允许使用invokespecial字节码指令来调用父类的方法 |
ACC_INTERFACE |
0x0200 | 标识这是一个接口 |
ACC_ABSTRACT |
0x0400 | 声明为abstract ,不能直接被实例化 |
ACC_SYNTHETIC |
0x1000 | 表示是由编译器产生的类,没有对应的源码 |
ACC_ANNOTATION |
0x2000 | 声明为annotation类型,注解 |
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_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 | bridge,方法由编译器产生 |
ACC_VARARGS |
0x0080 | 方法带有变长参数 |
ACC_NATIVE |
0x0100 | native,标识本地方法 |
ACC_ABSTRACT |
0x0400 | 声明为abstract ,没有具体实现 |
ACC_STRICT |
0x0800 | strictfp,方法使用FP-strict浮点格式 |
ACC_SYNTHETIC |
0x1000 | 表示是由编译器产生的类,没有对应的源码 |
索引信息
访问标识之后则是索引信息,有类索引、父类索引、接口索引,通过这三项数据来确定这个类的继承关系
类索引
用于确定这个类的全限定名,指向常量池中的常量
父类索引
用于确定这个类的父类的全限定名,指向常量池中的常量
接口索引
描述哪个类实现了哪些接口,按照implements的顺序从左到右排列在接口索引集合中,通过interfaces_count记录数量
字段表集合
1 | field_info{ |
字段计数器和字段表
字段包括类变量以及实例变量,不包括方法内部、代码块内部声明的局部变量,也不会列出从父类或者实现的接口中继承来的字段
字段的名称、类型是引用的常量池中的常量来进行描述的,常量池中描述了每个字段的完整信息,包括字段标识符、访问修饰符、static修饰、final修饰等
方法表集合
1 | method_info { |
方法表中存储的是一个一个的method_info,method_info结构可以表示类和接口中定义的所有方法,包括实例方法、类方法、实例初始化方法和类或接口初始化方法
属性表集合
1 | attribute_info { |
记录的是class文件所携带的辅助信息,如class文件的源文件名称,以及带有RetentionPolicy.CLASS或RetentionPolicy.RUNTIME的注解