虚拟机提供商发布了许多可以运行在各种不同平台的虚拟机,这些虚拟机都可以载入和执行同一种平台无关的字节码, 从而实现程序的“一次编译,到处运行”。虚拟机和字节码存储格式是构成平台无关性和语言无关性的基石。
编译方式
- 编译成二进制本地机器码
- 编译成与操作系统和机器指令集无关的、平台中立的格式作为程序编译后的存储格式
文件
Class 文件是一组以8位字节为基础单位的二进制流。Class文件格式采用一种类似于C语言结构体的伪结构来存储数据,这种伪结构中只有两种数据类型:无符号数和表。
- 无符号数:属于基本的数据类型,以u1,u2,u4,u8来表示1个字节、两个字节、四个字节和八个字节的无符号数,无符号数可以用来描述数字、索引信息、数量值或者按照UTF-8编码构成字符串值。
- 表:是由多个无符号数或者其他表作为数据项构成的复合数据类型。所有表都习惯以“_info”结尾。表描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表。
魔数
每个Class文件的头4个字节称为魔数,它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。
很多文件存储标准中都使用魔数来进行身份识别,例如图片格式gif\jpeg.
常量池
常量池可以理解为Class文件之中的资源仓库,是在Class文件中第一个出现的表类型数据项目。常量池入口为u2类型的常量池容量计数值。
- 字面量,如文本字符串、声明为final的常量值。
- 符号引用
- 类与接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符 当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。
访问标志
用于识别一些类或接口层次的访问信息,包括这个Class是类还是接口、是否为public类型、是否定义为abstract类型、是否为final类型。
类索引、父类索引和接口索引集合
确定这个类的继承关系
字段表集合
用于描述接口或者类中声明的变量,字段包括类变量(static修饰符)和实例级变量,但不包括在方法内部声明的局部变量
方法表集合
字节码指令
由一个字节长度的、代表着某种特定操作含义的数字(称为操作码)以及跟随其后的零至多个代表此操作所需参数而构成。由于Java虚拟机采用面向操作数栈的架构,所有大多数指令不包含操作数。
Class文件格式放弃了编译后代码的操作数长度对齐。
加载和存储指令
加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输
运算指令
用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作栈顶。
类型转换指令
将两种不同的数值类型进行相互转换。
对象创建和访问指令
Java虚拟机对类实例和数组的创建与操作使用了不用的字节码指令。
操作数栈管理指令
直接操作操作数栈的指令。
控制转移指令
可以让虚拟机有条件或无条件的从指定的位置指令而不是控制转移指令的下一条执行继续执行程序。
方法调用和返回指令
方法调用指令与数据类型无关,而方法返回指令是根据返回值类型区分的。
异常处理指令
程序中显式抛出异常的操作都由athrow指令来实现。
同步指令
Java虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor)来支持的。
命令
javap
javap 是 Java class文件分解器,可以反编译(即对javac编译的文件进行反编译),也可以查看java编译器生成的字节码。用于分解class文件
1
javap -v SynchronizedUse.class > output.txt
-p -private 显示所有类和成员。
1
javap -p JavapTest.class
-public 仅显示公共类和成员。