Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode) 以及跟随其后的零至多个代表此操作所需的参数(称为操作数,Operand)构成。由于Java虚拟机采用 面向操作数栈而不是面向寄存器的架构(这两种架构的执行过程、区别和影响将在第8章中探讨),所 以大多数指令都不包含操作数,只有一个操作码,指令参数都存放在操作数栈中。
1.1 字节码与数据类型
在Java虚拟机的指令集中,大多数指令都包含其操作所对应的数据类型信息。举个例子,iload指 令用于从局部变量表中加载int型的数据到操作数栈中,而fload指令加载的则是float类型的数据。这两 条指令的操作在虚拟机内部可能会是由同一段代码来实现的,但在Class文件中它们必须拥有各自独立 的操作码。
对于大部分与数据类型相关的字节码指令,它们的操作码助记符中都有特殊的字符来表明专门为哪种数据类型服务:i代表对int类型的数据操作,l代表long,s代表short,b代表byte,c代表char,f代表 float,d代表double,a代reference。也有一些指令的助记符中没有明确指明操作类型的字母,例如 arraylength指令,它没有代表数据类型的特殊字符,但操作数永远只能是一个数组类型的对象。还有另
外一些指令,例如无条件跳转指令goto则是与数据类型无关的指令。
因为Java虚拟机的操作码长度只有一字节,所以包含了数据类型的操作码就为指令集的设计带来了很大的压力:如果每一种与数据类型相关的指令都支持Java虚拟机所有运行时数据类型的话,那么 指令的数量恐怕就会超出一字节所能表示的数量范围了。因此,Java虚拟机的指令集对于特定的操作 只提供了有限的类型相关指令去支持它,换句话说,指令集将会被故意设计成非完全独立的。 (《Java虚拟机规范》中把这种特性称为“Not Orthogonal”,即并非每种数据类型和每一种操作都有对应的指令。)有一些单独的指令可以在必要的时候用来将一些不支持的类型转换为可被支持的类型。
Java虚拟机指令集所支持的数据类型
1.2 加载和存储指令
加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈(见第2章关于内存区域的介绍)之
间来回传输,这类指令包括:
·将一个局部变量加载到操作栈:iload、iload
·将一个数值从操作数栈存储到局部变量表:istore、istore
·将一个常量加载到操作数栈:bipush、sipush、ldc、ldcw、ldc2_w、aconst_null、iconst_m1、 iconst、lconst
·扩充局部变量表的访问索引的指令:wide 存储数据的操作数栈和局部变量表主要由加载和存储指令进行操作,除此之外,还有少量指令, 如访问对象的字段或数组元素的指令也会向操作数栈传输数据。