一、 无关性的基石
- 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式——字节码
- 平台无关性、语言无关性
- Java语言中的各种变量、关键字和运算符号的语义最终都是由多条字节码命令组合而成的
二、Class类文件的结构
- 任何一个Class文件都对应着唯一一个类或接口的定义信息,但反过来说,类或接口并不一定都得定义在文件里(譬如类或接口也可以通过类加载器直接生成)
- Class文件是一组以8字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符。当遇到需要占用8个字节以上空间的数据项时,则会按照高位在前的方式分割成若干个8位字节进行存储
- Class文件格式采用一种类似于C语言结构体的伪结构才存储数据,这种伪结构中只有两种数据类型:无符号数和表
- 无符号数属于基本的数据类型,以u1、u2、u4、u8来分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码构成字符串值
- 表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性以“info_”结尾。表用于描述有层次关系的复合结构的数据,整个Class文件本质上就是一张表。
- 无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式。
三、Class文件格式表
IMG_20170729_154046.jpg-
每个Class文件的头4个字节称为魔数,它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。紧接着4个字节存储Class文件的版本号:第5和第6个字节是次版本号,第7和第8个字节是主版本号
-
第9和第10个字节代表常量池容量计数值,为常量池的入口,这个计数从1开始而非从0开始。原因在于满足后面某些指向常量池的索引值的数据在特定情况下需要表达“不引用任何一个常量池项目”的含义。
-
常量池中主要存放两大常量:字面量和符号引用。(关于如何查看常量池,在书本P168-173)
- 字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为final的常量值等
- 符号引用属于编译原理方面的概念,包括以下三类常量:
- 类和接口的全限定名
- 字段的名称和描述符
- 方法的名称和描述符
-
常量池结束后,紧接着的2个字节代表访问标志,用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口;是否定义为public类型;是否定义为abstract类型;如果是类的话,是否被声明为final等。
-
在访问标志之后,紧接着的是类索引、父类索引和接口索引集合。
- 类索引和父类索引均用u2类型的索引值表示,各自指向一个类型为CONSTANT_Class_info的类描述符常量,通过CONSTANT_Class_info类型的常量中的索引值可以找到定义在CONSTANT_Class_info类型的常量中的全限定名字符串。
- 对于接口索引集合,入口的第一项——u2类型的数据为接口计数器,表示索引表的容量。如果该没有实现任何接口,则该计数器值为0,后面接口的索引表不再占用任何字节。
-
字段表集合
-
字段表用于描述接口或者类中声明的变量。字段包含类级变量以及实例级变量,但不包括方法内部声明的局部变量。
-
描述的信息有:字段的作用域(public、private、protected修饰符)、是实例变量还是类变量(static修饰符)、可变性(final)、并发可见性(volatile修饰符,是否强制从主内存读写)、可否被序列化(transient修饰符)、字段数据类型(基本类型、对象、数组)、字段名称。
-
全限定名、简单名称、描述符三者区分:
- 全限定名如“org/fenixoft/classzz/TestClass”是类TestClass的全限定名,仅仅是把类全名中的“.”替换成了“/”而已,为了使多了全限定名之间不产生混淆,在使用时最后一般会加入一个“;”表示全限定名结束。
- 简单名称是指没有类型和参数修饰的方法或者字段名称
- 描述符是用来描述字段的数据类型、方法的参数列表(包括数量、类型以及顺序)和返回值。
- 字段表集合中不会列出从超类或者父接口中继承而来的字段,但有可能列出原本Java代码之中不存在的字段
-
方法表集合
- 方法表的结构如同字段表一样,依次包括了访问标志(access_flags)、名称索引(name_index)、描述符索引(descriptor_index)、属性表集合(attributes)几项。方法里的Java代码经过编译器编译成字节码指令后,村放在方法属性表集合中一个名为“Code”的属性里面。
- 与字段集合相对应的,如果父类方法在子类中没有被重写,方法表集合中就不会出现来自父类的方法信息,但有可能出现由编译器自动添加的方法
- 特征签名:一个方法中各个参数在常量池中的字段符号引用的集合,但是不包括返回值。因此无法仅仅依靠返回值的不同来对一个已有方法进行重载。
-
四、字节码指令简介
-
Java虚拟机的指令是由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成。
-
指令集的操作码总数不可能超过256条;Class文件格式放弃编译后代码的操作数长度对齐,导致解释执行时性能有些损失,但代码短小精悍。
-
指令类型:
- 加载和存储指令
- 运算指令
- 类型转换指令
- 对象创建与访问指令
- 操作数栈管理指令
- 控制转移指令
- 方法调用和返回指令
- 异常处理指令
- 同步指令
-
私有实现虚拟机以获得更高性能、更低内存消耗或更好的可移植性的方式:
- 将输入的Java虚拟机代码在加载或执行时翻译成另外一种虚拟机的指令集
- 将输入的Java虚拟机代码在加载或执行时翻译成宿主机CPU的本地指令集(即JIT代码生成技术)