字节码指令简介

 java虚拟机的指令由一个字节长度的,代表某种含义的数字(操作码,Opcode),以及跟在后面的若干参数组成。由于java虚拟机采用基于栈的指令集机构,因此大多数指令都只有操作码,参数则存在于操作数栈中。

 字节码指令集只占1个字节,因此不会最多256条(0~255)。class文件编译后不会执行操作数长度对齐,因此在处理超过一个字节的数据时,必须重建出具体的数据结构,比如将1个16位长度的无符号整数使用2个无符号字节存起来。这种操作某种程度上会损失一些性能,但是由于放弃了长度对齐,可以节省大量的填充和符号间隔。

 忽略掉异常,Java虚拟机解释器的执行模型大概如下:

1
2
3
4
5
6
do{
pc寄存器值+1
根据pc寄存器指示的位置,从字节码流中取出操作码
if(字节码存在操作数) 从字节码流中取出操作数
执行操作码所定义的操作
}while(字节码流长度 > 0)

1. 加载和存储指令

 将局部变量加载到操作数栈:iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>均以下省略_n,代表槽

 将数值从操作数栈存储到局部变量表:istore、lstore、fstore、dstore、astore

 将常量加载到操作数栈:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst、lconst、fconst、dconst

 扩充局部变量表的访问索引指令:wide

2. 运算指令

 加法:iadd、ladd、fadd、dadd
 减法:isub、lsub、fsub、dsub
 乘法:imul、lmul、fmul、dmul
 除法:idiv、ldiv、fdiv、ddiv
 取余:irem、lrem、frem、drem
 取反:ineg、lneg、fneg、dneg
 位移:ishl、ishr、iushr、lshl、lshr、lushr
 按位或:ior、lor
 按位与:iand、land
 按位异或:ixor、lxor
 局部变量自增:iinc
 比较:dcmpg、dcmpl、fcmpg、fcmpl、lcmp

3. 类型转换指令

 java虚拟机直接支持小范围到大范围的安全转换,比如int -> long、float、double等。

显示转换指令:i2b、i2c、i2s、l2i、f2l、d2i、d2l、d2f

4. 对象创建指令

 创建类实例:new
 创建数组:newarray、anewarray、multianewarray
 访问类字段:getstatic、putstatic
 访问实例字段:getfield、putfield
 加载数组元素到操作数栈:baload、caload、saload、iaload、laload、faload、daload、aaload
 存储操作数栈的值到数组元素中:bastore、castore、sastore、iastore、lastore、fastore、dastore、aastore、
 取数组长度:arraylength
 检查实例类型:instanceod、checkcast

5. 操作数栈管理指令

 弹出操作数栈栈顶1到2个元素:pop、pop2
 复制栈顶1或2个元素并重新入栈:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2
 栈顶两个数值互换:swap

6. 控制转移指令

 条件分支:ifeg、iflt、ifne、ifgt、ifnull、ifnonnull、if_icmp eq、if_icmp ne、if_icmp lt、if_icmp gt、if_icmp le、if_icmp ge、if_acmp eq、if_acmp ne

 符合条件分支:tableswitch、lookupswitch

 无条件分支:goto、goto_w、jsr、jsr_w、ret

7. 方法调用和返回指令

  • 调用指令

invokevirtual:实例方法
invokeinterface:接口方法
invokeinspecial:构造方法、私有方法、父类方法
invokestatic:静态方法
invokedynamic:运行时动态解析出调用点限定符所引用的方法

  • 返回指令

ireturn、lreturn、freturn、dreturn、areturn注意:返回值为boolean、byte、char、short、int时采用ireturn指令

8. 异常指令

 显示抛出的异常:athrow
 catch中捕获的异常不是字节码指令实现的,而是采用异常表。

9. 同步指令

 方法级的同步无需字节码指令控制,它实现在方法调用和返回之中。当调用时会检查方法表结构中的ACC_SYNCHRONIZED权限,如果设置了则会先获取管程(锁),执行完毕释放管程。如果抛出异常且没有处理此异常,管程将会在异常抛到方法边界之外时自动释放。

  synchronized语句块指令:monitorenter、monitorexit


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!