Java虚拟机:class文件格式

本博文目的:通过本片博文的阅读,希望大家能够能class文件格式有一个直观的认识,同时对其中的一些实现细节也能有一定的掌握。本文并不会说的太细,太细节化的东西,真的太多,涉及到一大堆属性和验证的过程。我都看烦了,不仅记不住,而且也不是我们所关心的东西。

每一个class文件都对应着唯一一个类或接口的定义信息,每个class文件都由字节流组成,多字节的数据项总是按照big-endian(大端在前)的顺序进行存储。接下来将采用类似C语言结构体的伪结构来描述class文件格式。

ClassFile结构

这里写图片描述

  1. magic(魔数)
    magic的唯一作用是确定这个文件是否为一个能被虚拟机所接受的class文件。魔数数值固定为0xCAFEBABE(看到这个数值,是否想到了java的图标,咖啡。。),不会改变。
  2. minor_version(副版本号)、major_version(主版本号)
    若某个class文件的主版本号为M,副版本号为m,那么这个class文件格式版本号确定为M.m。
  3. constant_pool_count(常量池计数器)
    此值等于常量池表中的成员数加1。
  4. constant_pool[] (常量池)
    constant_pool是一种表结构,它包含class文件结构及子结构中引用的所有字符串常量、类或接口名、字段名和其他常量。常量池以1~constant_pool_count - 1为索引。
  5. access_flags
    access_flags是一种由标志所构成的掩码,用于表示某个类或者接口的访问权限及属性。
标志名含义
ACC_PUBLIC0x0001声明为public,可以从包外访问
ACC_FINAL0x0010声明为final,不允许有子类
ACC_SUPER0x0020当用到invokespecial指令时,需要对父类方法做特殊处理
ACC_INTERFACE0x0200该class文件定义的是接口而不是类
ACC_ABSTRACT0x0400声明为abstract,不能被实例化
ACC_SYNTHETIC0x10000声明为synthetic,表示该class文件并不是由Java源代码生成
ACC_ANNOTATION0x2000标识注解类型
ACC_ENUM0x4000标识枚举类型


6. this_class(类索引)
this_class的值必须是对常量池中某项的一个有效索引值。常量池在这个索引处的成员必须为CONSTANT_Class_info类型的结构体。
7. super_class(父类索引)
对于类来说,super_class的值要么是0,要么是对常量池表中某项的一个有效索引值。如果它不是0,那么常量池在这个索引处的成员必须为CONSTANT_Class_info类型常量。
8. interfaces_count(接口计数器)
interfaces_count表示当前类或接口的直接超接口的数量。
9. interfaces[] (接口表)
interfaces[]中的每个值都是常量池表中的某一项的有效索引值,它的长度为interfaces_count。每个成员interfaces[i]必须为CONSTANT_Class_info结构。
10. fields_count(字段计数器)
表示当前class文件fields表的成员个数。
11. fields[] (字段表)
fields表中的每个成员都必须是一个fields_info结构。
12. methods_count(方法计数器)
表示当前class文件methods表中的成员个数
13. methods[] (方法表)
methods表中的每个成员都必须是一个method_info结构。
14. attributes_count(属性计数器)
表示当前class文件属性表的成员个数。
15. attributes[] (属性表)
属性表的每个项的值都是attribute_info结构。

常量池

Java虚拟机指令不依赖于类、接口、类实例或数组的运行时布局,而是依赖常量池表中的符号信息。可见常量池是多么的重要。。

常量池表中的所有项都具有如下通用格式:

cp_info {
    u1 tag;
    u2 info[];
}

每个cp_info项都必须以一个表示cp_info类型的单字节“tag”项开头。后面info[]数组的内容由tag的值所决定。tag可取值如下所示:

常量类型
CONSTANT_Class7
CONSTANT_Fieldref9
CONSTANT_Methodref10
CONSTANT_InterfaceMethodref11
CONSTANT_String8
CONSTANT_Integer3
CONSTANT_Float4
CONSTANT_Long5
CONSTANT_Double6
CONSTANT_NameAndType12
CONSTANT_Utf81
CONSTANT_MethodHandle15
CONSTANT_MethodType16
CONSTANT_InvokeDynamic18

针对于上面的每一个值,都会有一种数据结构与之对应。比如当tag项的值为CONSTANT_Class时,对应的数据结构为:

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;//此值是对常量池表的一个有效索引。且该索引处的成员为CONSTANT_Utf8_info结构。
}

对于tag的其他值,大家可以查看相关的文章。。这里就不一一赘述了。。

字段

每个字段都由field_info结构所定义。field_info结构如下:

field_info {
    u2 access_flags;//表示字段的访问权限和基本属性
    u2 name_index;//对常量池的一个有效索引,表示一个有效的非限定名
    u2 descriptor_index;//对常量池的一个有效索引,表示一个有效的字段描述符
    u2 attributes_count;//表示当前字段的附加属性的数量
    attribute_info attributes[attributes_count];//属性的具体信息,其值为attribute_info结构
}

方法

所有的方法,包括实例初始化方法()和类或接口初始化方法()在内,都由method_info结构来定义。
method_info结构格式如下:

method_info {
    u2 access_flags;//用于定义当前放法的访问权限和基本属性
    u2 name_index;//常量池的一个有效的索引,要么表示一个特殊的方法,要么表示一个方法的有效非限定名。
    u2 descriptor_index;//常量池的一个有效索引,表示一个有效的方法的描述符
    u2 attributes_count;//表示当前方法附加属性的数量
    attribute_info attributes[attributes_count];//属性的值,且每一项值都需要是attribute结构。
}

属性

属性在class文件中ClassFile结构、field_info结构、method_info结构和Code_attribute结构都有使用。(这个属性太多了,我就只简单列举几个了,想继续研究的话可以再查查相关文档)
所有属性的通用格式如下:

attribute_info {
    u2 attribute_name_index;//对常量池的有效索引,表示当前属性的名字;
    u4 attribute_length;//给出了跟随其后的信息字节的长度
    u1 info[attribute_length];//信息的内容
}

下面给出一些常见的属性:


  • ConstantValue属性
    这个属性是定长属性,位于field_info结构的属性表中,ConstantValue表示一个常量表达式。
  • Code属性
    Code属性是变长属性,位于method_info结构的属性表中。Code属性中包含某个方法、实例初始化方法、类或接口初始化方法的Java虚拟机指令及相关辅助信息。
  • StackMapTable属性
    这个属性是变长属性,位于Code属性的属性表中。这个属性用在虚拟机的类型检查验证阶段。
  • Exceptions属性
    这个属性是变长属性,位于method_info结构的属性表中。Exceptions属性指出了一个方法可能抛出的受检异常。

还有许多的属性,就不一一列举了,有兴趣的可以查查相关文档。

格式检查

如果Java虚拟机准备加载某个class文件,那么它首先应该保证这个文件符合class文件的基本格式,这个过程称为格式检查。所需验证的事项有:

  • 前四个字节必须是正确的魔数
  • 能够辨识出来的所有属性都必须具备合适的长度
  • class文件的内容不能缺失,尾部不能有多余的字节
  • 常量池必须符合相关的各项约束

Java虚拟机代码约束

Java虚拟机将普通方法、实例初始化方法或类和接口的初始化方法的代码存储在class文件method_info结构里的Code属性的code数组中。这些代码需要满足静态约束结构化约束
静态约束是一系列用来定义文件是否编排良好的约束。
结构化约束是为了限定Java虚拟机指令之间的关系。

class文件校验

Java虚拟机会在链接的阶段对class文件进行校验以判断其是否满足必要的约束。链接期校验还有助于增强解释器的运行期执行性能。直观的理解,就是咱们定义了一些规则,虽然编译时检查通过了,谁知道是否准备运行时是否哪些规则遭到了破坏。因此需要重新校验下。比如,通过校验可以确保如下内容:

  • 操作数栈不会发生上限或下限溢出
  • 所有局部变量的使用和存储都是有效的
  • 所有Java虚拟机指令都拥有正确的参数类型等。

这个校验还涉及到很多内容,但是咱们要明白,虽然class文件有了,但也不是直接就拿来用的,还是需要校验一下地。。

总结:上面主要简单介绍了一下class文件结构,希望大家能对class文件的理解有了进一步的认识。

愿大家共同进步!!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值