Class 类文件的结构
概述
是一组以 8 位字节为基础单位的二进制流。
其格式是一种类似于 C 语言结构体的伪结构,由两种数据类型组成。
- 无符号数: 基本数据类型,数字、索引引用、数量值等等。
- 表: 由多个无符号数或者其他表组成,以 _info 结尾。
整个 Class 文件本质上就是一张表。
魔数与 Class 文件版本
每个 Class 文件的头 4 个字节被称为魔数( Magic Number )。
作用是确定这个文件是否能被虚拟机接受,例如 Gif,jpeg 之类的文件也存在魔数。因为通过后缀来判断不够安全。
Class 文件的魔数值为: OxCAFEBABE (其魔数值某种程度上也是决定了 Java 未来的图标是咖啡 ^ v ^ )
第 5、6、7、8 个字节分别是次版本号和主版本号。
常量池
Class 文件的资源仓库。
常量池入口放置一个 u2 类型的数据,用于统计池中数量。
( u1,u2,u4,u8 分别代表 1、2、4、8 个字节的无符号数)
池中主要存放两种类型的常量:
- 字面量: 像 Java 中的 String,final 修饰的常量。
- 符号引用: 类、接口、字段、方法、名称描述。JVM
JDK 1.7 中有 14 中常量类型,每一项都是一个表。
访问标志
是否为 public
,final
是类还是接口,是否抽象,是不是枚举等等。
类索引、父类索引、接口索引集合
字段表集合
用来描述类或接口中声明的变量,不包括局部变量。
字段作用域、是实例变量还是类变量、是否被 final
修饰等等。
方法表集合
描述方法,类似于字段的形式。
属性表集合
在字段表集合和属性表集合中都存在,用来描述某些场景的专有信息。
字节码指令
概述
JVM 的指令由一个字节长度的特定数字( Opcode )和数字后面带的参数( Operands )构成。
字节码与数据类型
在 Class 文件中对于不同的数据类型,采用不同的 Opcode 。
例如:
iload
用来操作 int 类型fload
用来操作 float 类型
但是由于 JVM 中 Opcode 的长度是有限制的,只有 1 个字节,所以为每个数据类型都设计相应的 Opcode 是不现实的。
于是使用一些单独的指令,将不支持类型操作的,转换成支持的。
加载和存储指令
栈帧中的局部变量表和操作数栈之间数据的相互传输。
运算指令
也是针对不同的类型,有着不同的 Opcode,例如:
- 加法:
iadd
、ladd
、fadd
、dadd
- 减法:
isub
、lsub
、fsub
、dsub
- 按位与指令:
ior
、lor
可以看出首字母就是数据类型。
类型转换指令
例如: i2b
i2c
i2s
等等
对象创建与访问指令
实例和数组采用不同的字节码指令。
操作数栈管理指令
例如:
- 将操作数栈的栈顶一个或两个元素出栈:
pop
、pop2
。 - 将栈最顶端的两个数值互换:
swap
。
控制转移指令
可以让指令不按顺序执行程序,本质上来说就是修改 PC Register 的值。
Java 中的保留字 goto
就是无条件分支中的控制转移指令。
方法调用和返回指令
例如:
invokestatic
用于调用类方法invokeinterface
用于调用接口方法
异常处理指令
JVM 中,处理 catch
语句不是由字节码指令来实现,是采用异常表来实现的。
同步指令
方法级的同步和方法内部的同步都是使用 Monitor 来支持的。
方法中调用过的每条 monitorenter
指令都必须执行对应的 monitorexit
指令。