Class文件结构

结构总览

-w851

Class文件中只有两种数据类型:无符号数和表:

  • 无符号数:属于基本的数据类型,以u1,u2,u4,u8代表对应的字节数;
  • 表:是由多个无符号数或者其他表作为数据项组合而成的复合数据结构。

1.magic(u4)

魔数,固定为0xCAFEBABE,唯一的作用是确定这个文件是否为一个能被虚拟机接受的class文件;

2.minor_version(u2) 和 major_version(u2)

minor_version:class文件次版本号;
major_version:class文件主版本号

3.constant_pool_count(u2) 和 constant_pool(cp_info)

由于常量池的数量是不固定的,所以需要在前面放置一个constant_pool_count(u2)表示常量池的数量(注意常量池的计数是从1开始的,设计者将第0项空出来是为了满足后面要表示“不引用任何一个常量池项目”)。

常量池主要存放两大类常量:

  • 字面量:比如文本字符串,声明为final的常量值,文件名等;
  • 符号引用:类和接口的全限定名;字段的名称和描述符;方法的名称和描述符.

Java代码在进行Javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态连接,根据实际运行的类型将对应的符号引用解析成具体的内存地址;也就是说,在Class文件中不会保存各个方法、字段的最终内存布局信息。

-w803

4.access_flags(u2)

类或接口的访问标志,包括:

  • 这个Class是类还是接口?
  • 是否public?
  • 是否abstract?
  • 是否final?

-w900

5.this_class(u2) 和 super_class(u2)

  • this_class:类索引;
  • super_class:父类索引;
    这两个索引都会指向常量池中类型为CONSTANT_Class_info,其中Object类的super_class为0。

6.interfaces_count(u2) 和 interfaces

  • interfaces_count表示接口数量;
  • interfaces是接口的集合,内部每个条目是u2类型指向常量池;

7.fields_count(u2) 和 fields

-w909

  • access_flags表示一些访问标识符
    -w884

  • name_index 表示字段的名称,指向常量池的下标;

  • descriptor_index 表示字段描述符,指向常量池的下标;
    -w901

  • attributes_count(u2) 和 attributes
    字段的属性集合,可以存储一些额外的信息,可以包含零个或多个;
    由于在Class级别也有attributes属性,后面再详细介绍。

8.methods_count(u2) 和 methods

方法的描述和字段的描述采用了一样的结构,如下所示:

-w895

和字段表不同的是,access_flags的取值不同,因为volatile和transient不能修饰方法,所以方法的access_flags中没有ACC_VOLATILE和ACC_TRANSIENT,同时方法相对于字段多了Synchronized、native、abstract等,所以多了ACC_SYNCHRONIZED、ACC_NATIVE、ACC_ABSTRACT等值.

-w882

还有一个最大的区别是:字段表的attribute里面多了一个Code字段;
方法体中的代码经过Javac编译后,最终变为字节码存储到Code属性内;只有拥有方法体的方法才会有Code属性,接口的方法和抽象方法是没有的。

-w905

这里介绍主要的属性:

  • max_stack:表示操作数栈(Operand Stacks)的最大深度,虚拟机运行的时候需要根据这个值栈帧(Stack Frame)中的操作数栈深度;
  • max_locals:表示局部变量表所需的空间,max_locals的单位是Slot
    Slot是虚拟机为局部变量分配内存所使用的最小单位;
    对于byte、char、float、int、short、boolean、returnAddress等长度不超过32位的数据类型,每个局部变量占用1个Slot;
    对于double和long这两种64位的数据类型占2个Slot。
    

方法参数(包括实力方法中的隐藏参数this)、try-catch语句中的catch块中所定义的异常、方法体中定义的局部变量都需要存放在局部变量表里面;
值得注意的:max_locals的值并不等于上述各项之和,原因是局部变量表中的Slot是可以重用的,当代码执行超出一个变量的作用域时,这个局部变量所占的Slot可以被其他局部变量使用。

  • code:用来存储编译后生成的字节码指令,每个指令就是一个u1类型的数据;
    字节码所对应的指令含义是虚拟机事先定义好的,当虚拟机读取到code中的一个字节码时,就可以找出对应的指令,配合操作数栈完成指令内容。

  • exception_table:用来表示显示的异常处理表,对应于代码中的try-catch语句;
    异常表实际上是Java代码的一部分,编译器使用异常表而不是简单的跳转命令来实现Java异常及finally处理机制;
    -w901
    其中包括了4个字段,表示含义为:虚拟机在执行字节码的时候,如果在[start_pc,end_pc)行之间出现了catch_type的异常,则程序会跳转到handler_pc行继续处理;
    当catch_type的值为0时,代表除了catch_type的任意异常情况都要跳转到handler_pc处继续进行。

  • Exceptions:表示方法可能抛出的异常;
    这里的Exceptions属性是在方法表中与Code属性平级的,不是上面的exception_table;

9.attributes属性

该属性可用的选项比较多,常用的有:

  • LineNumberTable:用于描述Java源代码和字节码行号的对应关系,不是运行时必须的;
  • LocalVariableTable:用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,不是运行时必须的;
  • SourceFile:Class文件的源码文件的文件名称;
posted @ 2020-04-12 14:27  再见理想_  阅读(310)  评论(0编辑  收藏  举报