与字节码的初次相遇

205 阅读4分钟

当你觉得达到了一定的技术瓶颈后,一定要抽空看看字节码,你会大有所获。 本节简单描述字节码文件的组成结构,旨在让大家对字节码有个大概的印象。

字节码是什么?

字节码是Java文件编译后生成的class文件。关于字节码,本人一直以来对其不太重视,有时候一看就眼困,但到了真正发自内心想要去了解去学,怀着好奇的心态去看书看规范,随着慢慢揭开字节码的面纱,才突然焕然大悟,大喊妙哉妙哉!!!

class文件结构

class文件结构按照固定顺序存储数据,数据的大小一般按1、2、4个字节区分,通常使用u1、u2、u4表示。存储的数据顺序为魔术、次版本号、主版本号、常量池计数器、常量池、访问标志、本类索引、父类索引、接口计数器、接口表、字段计数器、字段表、方法计数器、方法表、属性计数器、属性表。

class ClassFile {
    //魔数
    var magic: U4? = null
    //次版本号
    var minor_version: U2? = null
    //主版本号 52-java8
    var major_ersion: U2? = null
    //常量池计数器
    var constant_pool_count: U2? = null
    //常量池表
    var constant_pool: Array<CpInfo>? = null
    //访问标志
    var access_flags: U2? = null
    //类索引
    var this_class: U2? = null
    //父类索引
    var super_class: U2? = null
    //接口计数器
    var interfaces_count: U2? = null
    //接口表
    var interfaces: Array<U2>? = null
    //字段计数器
    var fields_count: U2? = null
    //字段表
    var fields: Array<field_info>? = null
    //方法计数器
    var methods_count: U2? = null
    //方法表
    var methods: Array<method_info>? = null
    //属性计数器
    var attributes_count: U2? = null
    //属性表
    var attributes: Array<attribute_info>? = null
}
  1. 魔数 u4

    一般文件都有自己的魔数,程序判断文件类型不是通过扩展名来判断,因为拓展名可以随意修改,而是通过魔数来确认文件类型,class文件的魔数是前四位字节表示的0xCAFEBABE(咖啡宝贝),这个魔数也是Java语言的创始人想到自己在爪哇岛喝过一种美味的咖啡而得来的灵感。

  2. 主次版本号 u4

    接下来四个字节表示主次版本号,前两个字节表示次版本号,后两字节表示主版本号,Java虚拟机实现规定本虚拟机所能支持的版本范围。

  3. 常量池计数器 u2

    接下来两个字节表示常量池计数器,常量池计数器减一等于常量池大小。 常量池的每一项都有相同的特征,第一个字节作为类型标志,tag,用于确定是哪种常量格式。

  4. 常量池

    常量池是表结构,常量池存储字符串常量、类名、接口名、字段名、方法名等其它常量。

  5. 访问标志 u2

    访问标志用于表示类或接口的访问权限及属性。

标志名含义
ACC_PUBLIC0x0001声明为public,包外可访问
ACC_FINAL0x0010声明final,不可被继承
ACC_SUPER0x0020当使用invokespecial指令时,需要对父类方法做特殊处理
ACC_INTERFACE0x0200定义为接口
ACC_ABSTRACT0x0400定义为抽象类,不可被实例化
ACC_SYNTHETIC0x1000声明synthetic,表示class文件不是Java源码生成
ACC_ANNOTATION0x2000注解类型
ACC_ENUM0x4000枚举类型
  1. this_class u2

    类索引,是常量池中有效的索引,并且常量类型为CONSTANT_Class_info。表示这个class文件定义的类或接口。

  2. super_class u2

    父类索引,为0或常量池中有效的索引,同上结构。表示这个类继承的直接父类。如果为0,则这个class文件只能表示Object,因为Object是唯一没有父类的类。

  3. 接口计数器 u2

    表示该类或接口实现的直接接口数量,两个字节表示。

  4. 接口表

    接口表存储该类实现的接口在常量池中的索引,常量类型为CONSTANT_Class_info,表内容顺序按源代码中从左到右的顺序存放,interfaces[0]是最左边实现的接口。

  5. 字段计数器 u2

    表示该类拥有的字段数量,也是两个字节表示。

  6. 字段表

    字段表存储该类各项字段信息,结构为fields_info。

  7. 方法计数器 u2

    表示该类或接口中定义的所有方法数量,方法包括实例初始化方法、类方法、实例方法、类或接口初始化方法。

  8. 方法表

    存储的每一项都是methods_info结构,如果该结构的access_flags标志没有设为ACCESS_ABSTRACT或ACCESS_NATIVE,则该结构中应包含方法实现所用的虚拟机指令。

  9. 属性计数器 u2

    表示该class文件属性表的成员个数。

  10. 属性数组

    表中每一项的结构都是attribute_info类型。

下一节会讲解各个结构详细存储的内容。