Java代码被前端编译器javac成功编译为一个符合Java虚拟机规范的字节码文件,然后JVM读取运行字节码文件。那么我们平常写的java代码编译后的字节码文件是怎样的呢?以及怎样的字节码文件符合规范的呢?
一般来说ClassFile文件结构如下;
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 interface_count;
u2 interfaces[interface_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];
}
注意:u4表示4个字节无符号,后缀_info表示一张表。
- magic:魔术,用于JVM校验字节码文件是否有效且合法。
- minor_version、major_version:表示字节码文件的版本号,如果字节码文件的版本号超出了JVM能够处理的范围,则JVM不会处理这个字节码文件。
- constant_pool_count和constant_pool:常量池计数器和常量池,常量池主要放置字面量和符号引用,常量池计数器则是来表示常量项的数量。
- access_flags:访问标志,主要是表示某一个接口或者类的访问权限,如:public,final,abstract等。
- this_class、super_class:分别表示类索引和父类索引,通过父类索引可以知道当前类的父类;类索引相当于当前类的全限定名。
- interface_count、interfaces:接口计数器用于表示当前类的或者接口的接口数量,接口表则表示接口的索引集合。
- methods_count和methods:方法计数器表示methods(方法表的数量),而methods表示一个数组集合,一个methods表描述了一个方法完整信息。
- attributes_count和attributes:属性计数器表示当前类属性表的数量,属性表是一个数组集合,属性表中每一项都是attribute_info的数据项。
常量池
常量池主要存放了两大类常量:字面量和符号引用。字面量类似于java的常量;而符号引用属于编译原理的概念了包括了类和接口的全限定名,字段的名称和描述符以及方法的名称和描述符。当JVM运行时,根据从常量池获得的符号引用,将类中的接口,方法等加载到具体的内存中。
访问标志
访问标志用于识别这个Class是类还是接口;是否定义为public、abstract还是final。访问标志一共有16个标志位可以用,当前值定义了其中的8个,没有使用到的要求为0。
类索引、父类索引和接口索引集合
类索引用于确定当前类的全限定名,父类索引用于确定当前类的父类全限定名,所以除了java.lang.Object以外,所有的java类的父索引都不为0.接口索引集合描述了这个类实现了哪些接口,按照implements的顺序排列。
字段表集合
字段表用于描述变量的信息。字段表的结构如下
类型 | 名称 | 数量 |
---|---|---|
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
access_flags用来描述字段的访问标志,name_index和descriptor_index分别代表了字段的名称和字段、方法的描述符。
attributes_count表示属性表集合大小,attributes表用来描述属性的额外信息。
方法表集合
方法表和字段表的结构几乎一模一样,如同字段表一样,方法表的结构依次包括了访问标志、名称索引、描述符索引、属性表等。
类型 | 名称 | 数量 |
---|---|---|
u2 | access_flags | 1 |
u2 | name_index | 1 |
u2 | descriptor_index | 1 |
u2 | attributes_count | 1 |
attribute_info | attributes | attributes_count |
仅仅在访问标志有些不同(有些变量的访问限制和方法不同,如方法里面没有volatile和transient修饰,而多了native、abstract等)。