2.1 字节码概述

Java虚拟机的指令由一个字节长度的操作码(opcode)和紧随其后的可选的操作数(operand)构成,如下所示。

        <opcode> [<operand1>, <operand2>]

比如将整型常量100压栈到栈顶的指令是bipush 100,其中bipush是操作码,100是操作数。字节码(bytecode)名字的由来是操作码的长度用一个字节表示。因为操作码长度只有一个字节长度,这使得编译后的字节码文件非常小巧紧凑,但也直接限制整个JVM操作码指令集的数量最多只能有256个,目前已经使用超过了200个。

大部分字节码指令是与类型相关的,比如ireturn指令用于返回一个int类型的数据,dreturn指令用于返回一个double类型的数据,freturn指令用于返回一个float类型的数据,这也使得字节码实际的指令类型远小于200个。

字节码使用大端序(Big-Endian)表示,即高位在前,低位在后的方式,比如字节码getfield 0002,表示的是getfiled 0x00<<8 | 0x02(getfield #2)。

字节码并不是某种虚拟CPU的机器码,而是一种介于源码和机器码中间的一种抽象表示方法,不过字节码通过JIT(Just In Time)技术可以被进一步编译成机器码。

根据字节码的不同用途,字节码指令可以大概分为如下几类:

❏ 加载和存储指令,比如iload将一个整型值从局部变量表加载到操作数栈;

❏ 控制转移指令,比如条件分支ifeq;

❏ 对象操作,比如创建类实例的指令new;

❏ 方法调用,比如invokevirtual指令用于调用对象的实例方法;

❏ 运算指令和类型转换,比如加法指令iadd;

❏ 线程同步,比如monitorenter和monitorexit这两条指令用于支持synchronized关键字的语义;

❏ 异常处理,比如athrow显式抛出异常。

字节码是运行在JVM上的,为了能弄懂字节码,需要对JVM的运行原理有所了解。接下来以Java虚拟机栈为切入点讲解字节码在JVM上执行的细节。