字节码指令简介

Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(操作码,Opcode)以及跟随其后的零至多个代表此操作所需的参数(操作数,Operand)构成。

Java虚拟机采用面向操作数栈而不是面向寄存器的架构,所以大多数指令都不包含操作数,只有一个操作码,指令参数都存放在操作数栈中。

字节码指令集的特点

  • 数量:Java虚拟机操作码的长度为一个字节(即0~255),这意味着指令集的操作码总数不超过256条

  • 操作数处理:Class文件格式放弃了编译后代码的操作数长度对齐,意味着虚拟机在处理那些超过

    一个字节的数据时,需要在运行时从字节中重建出具体数据的结构。譬如将一个16位长度的无符号

    整数使用两个无符号字节存储起来(假设将它们命名为byte1byte2),那么它们的值应该是

    (byte1 << 8) | byte2
    

    这种操作会导致解释执行字节码时损失一些性能,但优势也很明显,能省略大量的填充和间隔符号。

执行模式

不考虑异常处理

do {
    自动计算PC寄存器的值加1;
    根据PC寄存器指示的位置,从字节码流中去除操作码;
    if (字节码存在操作数) 从字节码流中去除操作数;
    执行操作码锁定义的操作;
} while (字节码流长度 > 0);

字节码与数据类型

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虚拟机所有运行时数据类型的话,那么指令的数量

恐怕会超过一字节能表示的数量范围了。因此只对特定的操作只提供有限的类型的指令去支持。

有一些单独的指令可以在必要的使用用来将一些不支持的类型转换为可被支持的类型。

编译器会在编译期或运行期将byteshort类型的数据代符号扩展(Sign-Extend)为相应的int类型数据,

booleanchar类型数据零位扩展(Zero-Extend)为相应的int类型数据。

数组也同理。最终处理byteshortbooleanchar的指令,实际上都是使用相应的int类型指令来进行的。

(没提供对应的指令就用int的)