Java 保持良好的向后兼容性,Class 文件结构的稳定功不可没
一、无关性基石
- Java 虚拟机不与包括 Java 语言在内的任何程序语言绑定,它只与“Class 文件”这种特定的二进制文件格式所关联
- Class 文件包含了Java 虚拟机指令集、符号表以及若干其他辅助信息
- 虚拟机丝毫不关心 Class 的来源是什么语言
二、类文件
- Java 保持良好的向后兼容性,Class 文件结构的稳定功不可没
- 任何一个 Class 文件都对应着唯一的一个类或接口的定义信息
- 反过来说,类或接口并不一定都得定义在文件里(可以动态生成,直接送入类加载器中)
- Class 文件是一组以 8 字节为基础单位的二进制流
- 要占用 8 字节以上空间的数据项时,按照高位在前的方式分割成若干个8字节进行存储
- 解析需要两种数据类型为基础
- 无符号数属于基本数据类型
- 描述数字、索引引用、数量值或者按照 UTF-8 编码构成字符串
- 表是由多个无符号数或者其他表作为数据项构成的复合数据类型(结尾以“_info”)
- 无符号数属于基本数据类型
- 要描述同一类型但数量不定的多个数据时,会使用一个前置的容量计数器
- 一系列连续的某一类型的数据为某一类型的“集合”
- 魔数与 Class 文件的版本
- Class 文件的头 4 个字节被称为魔数
- 用来确定这个文件是否为一个能被虚拟机接受的 Class 文件
- 第 5、6 个字节是次版本号,第 7、8 个字节是主版本号
- Java 版本号是从 45 开始的,高版本能向下兼容,但是不能向上兼容
- Class 文件的头 4 个字节被称为魔数
三、常量池
- Class 文件里的资源仓库,是 Class 文件中第一个出现的表类型数据项目
- Class 文件只有常量池的容量计数是从 1 开始,其他都是从 0 开始
- 主要存放
- 字面量
- 文本字符串、被声明为 final 的常量值
- 符号引用
- 被模块导出或者开放的包
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
- 方法句柄和方法类型
- 动态调用点和动态常量
- 字面量
- Java 代码在进行 Javac 编译的时候,虚拟机加载 Class 文件的时候进行动态连接
- 虚拟机做类加载的时候,会从常量池获得对应的符号引用,在类创建时或运行时解析、翻译到具体的内存地址中
- 常量池中每一项常量都是一个表
- 分析 Class 文件字节码的工具:javap
四、访问标志
- 在常量池之后,紧接着的 2 个字节代表访问标志(access_flags)
- 主要标识类或接口层次的访问信息,类还是接口、public、abstract、final等
- access_flags 一共有 16 个标志位可以使用
- 类索引、父类索引与接口索引
- 三项数据用来确定该类型的继承关系
- 类索引:确定这个类的全限定名
- 父类索引:确定这个类的父类全限定名
- 接口索引:描述这个类实现了哪些接口
五、表集合
- 字段表集合
- 用于描述接口或者类中声明的变量
- 可以包括修饰符有字段的作用域(public、private、protected修饰符)、实例变量还是类变量、可变性、并发可见性、字段数据类型等
- 全限定名:把类全名中的“.”替换成“/”
- 简单名称:inc()方法和m字段分别为:“inc”和“m”
- String[][] => [[Ljava/lang/String
- int[] => [I
- 方法表集合
- 结构:访问标志access_flags,名称索引name_index,描述符索引descriptor_index,属性表集合attributes
- 属性表集合
- Class文件、字段表、方法表都可以携带自己的属性表集合
- 不再要求各个属性表具有严格顺序,只要不与已有属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息
六、字节码指令简介
- 字节码指令集可算事一种具有鲜明特点、优势和劣势均很突出的指令集架构
- 限制了 Java 虚拟机操作码的长度为一个字节,指令集的操作码总数不能够超过 256 条
- 字节码与数据类型
- iload 指令用于从局部变量表中加载 int 型的数据到操作数栈,fload 则是 float 类型
- Java 虚拟机的指令集对于特定的操作只提供了有限的类型相关指令去支持
- 加载和存储指令
- 加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输
- 存储数据的操作数栈和局部变量表主要由加载和存储指令进行操作
- 运算指令
- 加法指令:iadd、ladd、fadd、dadd
- 减法指令、乘法指令…
- 其他
- 类型转换指令
- 操作数栈管理指令
- 控制转移指令
- 方法调用和返回指令
- 异常处理指令
- 同步指令
七、共有设计、私有实现
- 虚拟机实现的方式
- 将输入的 Java 虚拟机代码在加载时或执行时翻译成另一种虚拟机的指令集
- 将输入的 Java 虚拟机代码在加载时或执行时翻译成宿主机处理程序的本地指令集(即即使编译器代码生成技术)