Class字节码文件的组成【上篇】

98 阅读4分钟

前言


  java的特点是跨平台性,而跨平台的运行标准是Class字节码文件,Class字节码是提供平台无关性的基础模型,使我们无须考虑如何兼容异构系统,只须被JVM识别即可。

概述


  字节码是Java虚拟机中的核心,是Java源代码的一种中间表示形式。简单来说,字节码就是将Java源代码转换为二进制格式后的中间代码。JVM通过将Java源代码编译成字节码,再通过字节码解释器将其转换为可以在不同平台上运行的本地机器码,从而实现Java跨平台的能力。

  字节码是一种中间代码,其本身并不直接被执行,而是需要经过JVM的解释或编译才能成为可执行的本地机器码。JVM在执行字节码时,会根据不同的阶段进行处理:在编译阶段,将字节码编译成本地机器码;在运行阶段,直接执行编译后的本地机器码。

  Class文件是一组以8位字节为基础单位的二进制字节流,所有16位、32位和64位长度的数据将被构造成2个、4个和8个字节单位来描述。

基本结构


  Class文件采用类似于C语言结构体伪结构来存储数据,只有两种数据类型

  • 无符号数

    • 基本数据类型,以u1,u2,u4,u8来分别表示一个字节,两个字节,四个字节和8个字节
    • 复合结构。由多个无符号数或者其他表作为数据项构成的类型,每个表都以"_info"结尾

    • 主要用于描述有层次关系的复合结构数据,整个Class文件本质上可以看成一张表

总体结构如下图所示:

image.png

注意: 上图Class文件中的数据项,无论顺序是数量,甚至于数据存储的字节序这样的细节,都是被严格限定的。哪个字节代表什么含义,长度是多少,先后顺序如何,都不允许改变

魔数头


  Class文件的1-4个字节代表该文件的魔数头。(用于标记该文件格式)Class文件格式的魔数值固定为“0xCAFEBABE” 无法修改

  唯一功能: 判断该文件格式是否为一种能被虚拟机接受的Class文件。(基于安全方面的考虑,在做软件的文件上传功能的时候,一定是要读取内容判断魔数而不是判断后缀名。)

版本号


  魔数头后的4个字节存储的就是Class文件的版本号(包括副版本号minor_version和主版本号major_version),其中5,6个字节表示Class文件的副版本号。7,8个字节表示Class文件的主版本号。主版本号和副版本号共同确定类文件格式的版本。

  JVM实例只提供特定范围的主版本号(Mo-Mn)和0至特定范围内(0至m)的副版本号。 版本对于关系如下表:

JDK版本副版本号主版本号十进制
JDK1.20000002E46
JDK1.30000002F47
JDK1.40000003048
JDK1.50000003149
JDK1.60000003250
JDK1.70000003351
JDK1.80000003452

常量池


版本号之后是常量池相关的数据项,主要包含俩部分:

  • 常量池索引计数器
    • u2的无符号数,主要用于记录常量池中相关元素数量,值只在大于0且小于constant_pool_count时才有效。
  • 常量池元数据
    • 字面量

      • 文本字符串
      • 声明为final的常量值
      • 等….
    • 符号引用

      • 类和结构的全限定名(类元数据,接口元数据)
      • 字段的名称和描述符(字段元数据,字段符号引用)
      • 方法的名称和描述符(方法元数据,方法符号引用)
      • 属性元数据
      • 对常量池中数据项的引用,此外常量池中各个项也会互相引用
      • 字节码指令中也存在对常量池的引用。

1698238190338.jpg

再来看CONSTANT_Class_info ,结构如下图:

image.png 由于已经确定了数据的表结构,所以他并没有tag,所以0x0004就表示后面的index,即指向全限定名的常量项目的索引,而他指向第四个常量

访问标志


  紧接着常量池后面的两个字节(u2类型)就表示访问标志位,它是种掩码标志,用于表示某个类或者接口的访问信息及基础属性。具体含义如下图:

1698239075416.png