class文件基本数据结构:
使用javac编译后的文件是.class文件,程序运行时,class文件被classLoader加载到JVM成为class对象,随后便可以创建该类的对象。
class文件是二进制文件流,记录类相关信息,可以被JVM解析。class文件内容并不复杂,结构如下:
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];
}
magic:魔数,4bytes,固定的cafebabe,简单校验,另外也可作为流传输时的解析头。
minor_version:次版本
major_version:主版本
constant_pool_count:常量池元素个数,指导解析多少常量元素,实际个数是constant_pool_count - 1,2bytes意味着组多65535个常量了。
constant_pool:常量池元素
cp_info {
u1 tag;
u1 info[];
}
记录的格式大体上是tv和tlv的模式,其中tag有以下几种:
CONSTANT_Class | 7 | (类或接口名称,是一个index,指向常量池中String类型常量) |
CONSTANT_Fieldref | 9 | (字段名称,类型,归属类) |
CONSTANT_Methodref | 10 | (记录方法调用常量名称,含归属类,方法名称,描述(含入参和返回值,例如 (Ljava/util/Date;Ljava/lang/Integer;)Ljava/lang/String),入参是date和integer,返回值string) |
CONSTANT_InterfaceMethodref | 11 | (使用接口调用方法) |
CONSTANT_String | 8 | (字符串常量) |
CONSTANT_Integer | 3 | (整数常量) |
CONSTANT_Float | 4 | (浮点数常量) |
CONSTANT_Long | 5 | (长整型常量) |
CONSTANT_Double | 6 | (double常量) |
CONSTANT_NameAndType | 12 | (名称和描述常量,被method_ref,field_ref等引用,记录扩展信息) |
CONSTANT_Utf8 | 1 | (字符串值) |
CONSTANT_MethodHandle | 15 | (函数句柄,类反射功能) |
CONSTANT_MethodType | 16 | (配合MethodHandle使用,描述方法入参和返回值) |
CONSTANT_InvokeDynamic | 18 | (描述invokedynamic的方法) |
access_flags:2bytes,描述类的访问权限,不同位具有不同含义,具体见java.lang.reflect.Modifier.isPublic(mod)相关的判断方法。
this_class:2bytes,寻址常量池中多有条目,记录当前类名称在cp中的索引。
super_class:2bytes,记录父类名称在cp索引,父类只能有一个。
interfaces_count:继承接口个数,2bytes。
interfaces:继承接口名称在cp里的引用。
fields_count:成员变量个数,2bytes。
fields:成员变量详情:
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
access_flags:同class上的access_flags,可选种类不同
name_index:名称在cp里的索引
descriptor_index:类型,泛型会被最小化到明确类
attributes_count:成员变量属性个数
attribute_info:属性具体内容,能用在field上的attribute有:
methods_count:方法个数
methods:方法详情
method_info
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
access_flags:同class上的access_flags,可选种类不同
name_index:名称在cp里的索引
descriptor_index:方法描述,含入参和返回值类型,例如
方法:
Object m(int i, double d, Thread t) {...}
描述:
(IDLjava/lang/Thread;)Ljava/lang/Object;
attributes_count:属性个数
attribute_info:属性具体内容,能用在method上的attribute有:
attributes_count:类属性个数
attributes:类具体属性详情,能作为类的attr有以下一些:
class中注解的数据格式:
annotation {
u2 type_index;
u2 num_element_value_pairs;
{ u2 element_name_index;
element_value value;
} element_value_pairs[num_element_value_pairs];
}
type_index:泛型类型索引,指向cp中ConstantUtf8类型常量,记录泛型类全名。
num_element_value_pairs:枚举属性字段个数
element_value_pairs:枚举中的多个属性,kv结构
element_name_index:属性的key索引
element_value:一个元素
class中的元素值element_value:
element_value被很多处复用。
element_value {
u1 tag;
union {
u2 const_value_index;
{ u2 type_name_index;
u2 const_name_index;
} enum_const_value;
u2 class_info_index;
annotation annotation_value;
{ u2 num_values;
element_value values[num_values];
} array_value;
} value;
}
tag:value类型,有以下种类,占用1byte枚举:
B | byte | const_value_index | CONSTANT_Integer |
C | char | const_value_index | CONSTANT_Integer |
D | double | const_value_index | CONSTANT_Double |
F | float | const_value_index | CONSTANT_Float |
I | int | const_value_index | CONSTANT_Integer |
J | long | const_value_index | CONSTANT_Long |
S | short | const_value_index | CONSTANT_Integer |
Z | boolean | const_value_index | CONSTANT_Integer |
s | String | const_value_index | CONSTANT_Utf8 |
e | Enum type | enum_const_value | Not applicable |
c | Class | class_info_index | Not applicable |
@ | Annotation type | annotation_value | Not applicable |
[ | Array type | array_value | Not applicable |
class中的codeAttribute:
Code_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 max_stack; // 方法的最大堆栈深度
u2 max_locals; // 方法的最大局部变量表
u4 code_length; // 字节码长度
u1 code[code_length]; // 字节码
u2 exception_table_length; // 异常表个数
{ u2 start_pc; // try开始位置
u2 end_pc; // try结束位置
u2 handler_pc; // catch位置
u2 catch_type; // 能被catch的类型
} exception_table[exception_table_length]; // 异常表实体(程序发生异常时,查异常表决定如何处理)
u2 attributes_count; // attribute个数
attribute_info attributes[attributes_count]; // attribute详情
}
codeAttribute能使用的attribute特别说一下:
1. LineNumberTable,字节码和源码具体行的对应关系。可选的attr,javac命令对应参数lines可以选择是否要将行号信息记录到class文件里,默认情况下javac会执行,详情见《java编译模式》。
2. LocalVariableTable,本地变量表,记录局部变量生命周期,名称,类型,对应frame中局部变量表变量的index。
3. LocalVariableTypeTable,本地变量表的补充,记录局部变量中泛型(Type类型)信息。
4. StackMapTable,栈图,1.6引入,用于优化类型校验。
5. RuntimeVisibleTypeAnnotations, RuntimeInvisibleTypeAnnotations,type注解。
参考:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7