目录
1. 概述
可以选择与操作系统和机器指令集无关、平台中立的格式作为程序编译后的存储格式。
2. 无关性基石
- 平台无关性
不同平台的虚拟机和所有平台都统一使用字节码程序存储格式。
- 语言无关性
3. 类文件结构
Class文件:一组以8为直接为基本单位的二进制流。采用类似C的伪结构来存储数据。
伪结构中只有两种数据类型:
- 无符号数:u1,u2,u4,u8。
- 表:描述层次关系的复杂结构的数据。
3.1 魔数和Class文件版本
- 魔数:身份识别,基于安全方面的考虑。0xCAFFBABE
- 版本号:次版本号+主版本号
虚拟机必须拒绝执行超过其版本号的Class文件。
3.2 常量池
资源仓库。
3.2.1 常量池容量计数
- 从1而不是从0开始。
- 0空出来的目的是满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不需要引用任何一个常量池项目”。
3.2.2 常量池
两大常量:
- 字面量
文本字符串以及什么为final的常量值等。
- 符号引用
<1>类和就诶口的全限定名
<2>字段的名称和描述符
<1>方法的名称和描述符
3.3 访问标志
是类还是接口?是否public?是否final?是否abstract?等判断
3.4 类索引、父类索引、接口索引集合
- 这里的缩影都是指向常量池的。
- 除去java.lang.Object外所有Java类的父类索引都不为0.
- 接口索引集合第一项会有一个接口计数器。
3.5 字段表集合
- 描述接口或者类中声明的变量。
- 字段包括类级变量以及实例级变量,不包括内部声明的局部变量。
会有字段修饰符:表示作用域、可变性、类变量还是实例变量等。
简单名称、描述符和全限定名:
- 简单名称:对于 <int m> 即为 <m>
- 全限定名:类的全名中的“.”换为“/”。如<java/lang/String>
- 描述符:
<java.lang.String[][]>变为 < [[Ljava/lang/String>
<int[]>变为 <[I>
字段表集合中不会累出超类或者父接口中继承而来的字段。
3.6 方法表集合
- 和字段表很类似。
- java代码进编译器编译成字节码指令后存放在方法属性表集合中一个名为“Code”的属性里。
- 如果弗雷的方法在子类中没有被重写,在方法表集合中不会出现父类方法
- 可能出现编译器自己添加的方法:类构造器“<clinit>”和实例构造器"<init>"
3.7 属性表集合
3.7.1 Code 属性
- 操作栈深度最大值
- 局部变量所需存储空间
- 异常处理表
3.7.2 Exceptions属性
- thows关键字后面列举的异常。
3.7.3 LineNumberTable属性
- 源代码行号和字节码行号之间的对应关系。
3.7.4 LocalVariableTable属性
- 栈帧中局部变量表中的变量和Java源码中定义的变量之间的关系
3.7.5 SourceFile属性
- 源文件名称
3.7.6 ConstantValue属性
- 通知虚拟机自动为静态变量赋值。
- 属性值只限与基本类型和String
3.7.7 InnerXlasses属性
- 记录内部类与数组之间的关联
3.7.8 Deprecated及Scynthetic属性
3.7.9 StackMapTable属性
- 代替以前比较消耗性能的基于数据流分析的类型推导验证器。采用类型检查
3.7.10 Signature属性
- 弥补缺陷:Java泛型运行期将泛型类型与用户定义的普通类型同等对待。运行期做反射无法得到泛型类型。
3.7.11 BootstrapMethods属性
4. 字节码指令
Java虚拟机采用面向操作数栈架构。
4.1 字节码与数据类型
- 大部分资料都包含其曹祖说对应的数据类型信息。(iload 表示局部变量表中加载int数据到操作数栈中)
- 并非每一种数据类型和每一种操作都有对应的指令。(JVM的操作码长度只有一个字节,所以不能实现所有组合)
4.2 加载和存储指令
- 加载,局部变量价值到操作栈:Tload
- 存储,从操作栈存储到局部变量表:Tstore
- 等。。
4.3 运算指令
加减乘除、取余取反、位运算等
4.4类型转换指令
数字类型的窄化指令永远不会导致JVM抛出运行时异常
4.5 控制转移指令
- 条件分支、复合条件分支以及无条件分支。
- 各种类型的比较最终都会转化为int的比较,JVM中int的条件分支最为丰富和强大
4.6 异常处理指令
采用异常表处理。
4.7 其他
- 对象创建于访问指令
- 操作数栈管理指令
- 方法调用和返回指令
- 同步指令