• Class类文件的结构


    1、Class文件是一组以8位字节为基础单元的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件中,中间没有任何的分隔符,这使得整个Class文件中存储的内容几乎全部都是程序运行的必要数据,没有空隙存在。当遇到需要占用8位字节以上空间的数据项时,则会按照高位在前的方式分割为若干个8位字节进行存储。

    2、按照Java虚拟机规定,Class文件格式采用一种类似C语言结构体的伪机构来存储,这种伪结构中只有两种数据类型:无符号数和表。

    3、无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值、或者按照UTF-8编码构成字符串值。

    4、表是由多个无符号数或其他表作为数据项构成的复合数据类型,所有表都习惯性地以"_info"结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表,它由如下所示的数据项构成。

    Class文件格式
    类型 名称 数量
    u4   magic 1
    u2 minor_version 1
    u2 major_version 1
    u2 constant_pool_count 1
    cp_info constant_pool constant_pool_count-1
    u2 access_flags 1
    u2 this_class 1
    u2 super_class 1
    u2 interface_count 1
    u2 interfaces interface_count
    u2 fields_count 1
    field_info fields fields_count
    u2 methods_count 1
    method_info methods methods_count
    u2 attributes_count 1
    attribute_info attributes attributes_count

     5、cp_info,常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。字面量比较接近于Java语言层面的常量概念,人文本字符串、被声明为

          final的常量值等。而符号引用则属于编译原理方面的概念,包括了下面三类常量:

    • 类和接口的全限定名(Fully Qualified Name)
    • 字段的名称和描述符(Descriptor)
    • 方法的名称和描述符    

         常量池中的每一项常量都是一个表,共有11种结构各不相同的表结构数据,这11种表都有一个共同的特点,就是表开始的第一位是一个url类型的标志符(tag,取值为1至12,缺少标志位2的数据类型),代表当前这个常量属于哪种常量类型,11种常量类型所代表的具体含义如下:

    常量池的项目烈性
    类型 标志 描述
    CONSTANT_Utf8_info 1 UTF-8编码的字符串
    CONSTANT_Integer_info 3 整型字面量
    CONSTANT_Float_info 4 浮点型字面量
    CONSTANT_Long_info 5 长整型字面量
    CONSTANT_Double_info 6 双精度浮点型字面量
    CONSTANT_Class_info 7 类或接口的符号引用
    CONSTANT_String_info 8 字符串类型字面量
    CONSTANT_Fieldref_info 9 字段的符号引用
    CONSTANT_Methodref_info 10 类中方法的符号引用
    CONSTANT_InterfaceMethodref_info 11 接口中方法的符号引用
    CONSTANT_NameAndType_info 12 字段或方法的部分符号引用

       之所以说常量池是最繁琐的数据,是因为这11种常量类型各自均有自己的结构。11种常量池的结构定义总结如下:

    常量池中11种数据类型的结构总表
    常量 项目 类型 描述
    CONSTANT_Utf8_info tag u1 值为1
    length u2 UTF-8编码的字符串占用了字节数
    bytes u1 长度为length的UTF-8编码的字符串
    CONSTANT_Integer_info tag u1 值为3
    bytes u4 按照高位在前存储的int值
    CONSTANT_Float_info tag u1 值为4
    bytes u4 按照高位在前存储的float值
    CONSTANT_Long_info tag u1 值为5
    bytes u8 按照高位在前存储的long值
    CONSTANT_Double_info tag u1 值为6
    bytes u8 按照高位在前存储的double值
    CONSTANT_Class_info tag u1 值为7
    index u2 指定全限定名常量项的索引
    CONSTANT_String_info tag u1 值为8
    index u2 指定字符串字面量的索引
    CONSTANT_Fieldref_info tag u1 值为9
    index u2 指定声明字段的类或接口描述符CONSTANT_Class_info的索引项
    index u2 指向字段描述符CONSTANT_NameAndType的索引项
    CONSTANT_Methodref_info tag u1 值为10
    index u2 指定声明方法的类描述符CONSTANT_Class_info的索引项
    index u2 指定名称及类型描述符CONSTANT_NameAndType的索引项
    CONSTANT_InterfaceMethodref_info tag u1 值为11
    index u2 指向声明方法的接口描述符CONSTANT_Class_info的索引项
    index u2 指定名称及类型描述符CONSTANT_NameAndType的索引项
    CONSTANT_NameAndType_info tag u1 值为12
    index u2 指向该字段或方法名称常量项的索引
    index u2 指向该字段或方法描述符常量项的索引

        可以使用javap工具的-verbose参数输出TestClass.class文件的字节码内容。

     6、字段表(field_info)用于描述接口或类中声明的变量。字段(field)包括了类级变量或实例级变量,但不包括在方法内部声明的变量。字段表结构如下:

    字段表结构
    类型 名称 数量
    u2 access_flags 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_count

    7、方法表的结构同字段表一样,仅在访问标志和属性表集合的可选性中有所区别。

    方法表结构
    类型 名称 数量
    u2 access_flags 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 attributes_count 1
    attribute_info attributes attributes_count

    8、属性表(attribute_info)在Class文件、字段表、方法表中都可以携带自己的属性表集合,用于描述某些场景专有的信息。

         与Class文件中其他数据项目要求严格的顺序、长度和内容不同,属性表集合的限制稍微宽松了一些,不再要求各个属性表具有严格的顺序,并且只要不与已有的属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息,java虚拟机运行时会忽略掉它不认识的属性。为了能正确地解析Class文件,《Java虚拟机规范(第二版)》中预定义了9中虚拟机应当能识别的属性,具体如下所示:

    虚拟机规范预定义的属性
    属性名称 使用位置 含义
    Code 方法表 Java代码编译成的字节码指令
    ConstantValue 字段表 final关键字定义的常量值
    Deprecated 类、方法表、字段表 被声明为deprecated的方法和字段
    Exceptions 方法表 方法抛出的异常
    InnerClasses 类文件 内部类列表
    LineNumberTable Code属性 Java源码的行号与字节码指令的对应关系
    LocalVariableTable Code属性 方法的局部变量描述
    SourceFile 类文件 源文件描述
    Synthetic 类、方法表、字段表 标识方法或字段为编译器自动生成的

      Code:Java程序方法体里面的代码经过javac编译器处理之后,最终变为字节码指令存储在Code属性表内。Code属性出现在方法表的属性集合之中,但并非所有的方法都必须存在这个属性,譬如接口或抽象方法就不存在Code属性,如果方法表有Code属性存在,那么它的结构将如表所示:

    Code属性表的结构
    类型 名称 数量
    u2 attribute_name_index 1
    u4 attribute_length 1
    u2 max_stack 1
    u2 max_locals 1
    u4 code_length 1
    u1 code code_length
    u2 exception_table_length 1
    exception_info exception_table exception_table_length
    u2 attributes_count 1
    attribute_info attributes attributes_count

     LineNumberTable:LineNumberTable属性用于描述Java源码行号与字节行号(字节码的便宜量)之间的对应关系。它并不是运行时必需的属性,但默认会生成到Class文件之中,可以在javac中使用-g:none或-g:lines选项来取消或要求生成这项信息。如果选择不生成LineNumberTable属性,对程序运行 产生的最主要的影响就是在抛出异常时,堆栈中将不会显示出错的行号,并且在调试程序的时候无法按照源码来设置断点。LineNumberTable属性的结构如下表所示:

    LineNumberTable属性结构
    类型 名称 数量
    u2 attribute_name_index 1
    u4 attribute_length 1
    u2 line_number_table_length 1
    line_number_info line_number_table line_number_table_length

          其中line_number_table是一个数量为line_number_table_length,类型为line_number_info的集合,line_number_info表包括了start_pc和line_number两个u2类型的数据项,前者是字节码行号,后者是Java源码行号。

    line_number_info属性结构
    类型 名称 数量
    u2 start_pc 1
    u2 line_number 1

      LocalVariableTable:LocalVariableTable属性用于描述栈帧中局部表量表中的变量与Java源码中定义的变量之间的关系,它不是运行时必需的属性,默认页不会生成到Class文件之中,可以再Javac中使用-g:none或-g:vars选项来取消或要求生成这项信息。如果没有生成这项属性,最大的影响是当其他人引用这个方法时,所有的参数名称都将丢失,IDE可能会使用诸如arg0、arg1之类的占位符来代替原有的参数名,这对程序运行没有影响,但是会给代码编写带来较大的不便,而且在调试期间调试器无法根据参数名称从运行上下文中获得参数值。LocalVariableTable属性的结构如下表所示:

    LocalVariableTable属性结构
    类型 名称 数量
    u2 attribute_name_index 1
    u4 atribute_length 1
    u2 local_variable_table_length 1
    local_variable_info local_variable_table local_variable_table_length

            其中local_variable_info项目代表了一个栈帧与源码中的局部变量的关联,start_pc和length属性分别代表了这个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖的长度,两者结合起来就是这个局部变量在字节码之中的作用范围。

             name_index和descriptor_index都是指向常量池中CONSTANT_Utf8_info型常量的索引,分别代表了局部变量的名称及局部变量的描述符。结构如下表所示。

    local_variable_info项目结构
    类型 名称 数量
    u2 start_pc 1
    u2 length 1
    u2 name_index 1
    u2 descriptor_index 1
    u2 index 1

          index是这个局部变量在栈帧局部变量表中Slot的位置。当这个变量的数据类型时64位类型时(double和long),它占用的Slot为index和index+1两个位置。

  • 相关阅读:
    Linux中conda安装R出现 package ‘utils’ in options("defaultPackages") was not found?
    生物信息软件/流程dockerfile打包参考
    非模式生物的GATK BQSR and VQSR
    【动植物研究动态】20220529文献解读
    基因组云计算书籍推荐:Genomics in the Cloud Using Docker, GATK, and WDL in Terra
    科技论文写作培训(蛋白质组方向)
    Docker容器yum报错连不上网:curl#6 "Could not resolve host: mirrorlist.centos.org; Unknown error"?
    Codeforces Round #782 (Div. 2)
    Codeforces Round #791 (Div. 2)
    sqlserver 判断是否存在,如果存在就update,不存在就insert
  • 原文地址:https://www.cnblogs.com/xuelu/p/3837631.html
Copyright © 2020-2023  润新知