描述
本文摘自深入理解Java虚拟机中关于字节码的介绍,部分指令参考,oracle字节码指令集。
数据类型:byte、short、int、long、float、double、char、boolean、reference。操作码以首字符为前缀,reference以a为前缀。例如iload,加载int到栈顶、aload加载reference到栈顶。
加载和存储指令
加载和存储指令用于将数据在栈帧中的局部变量表和操作数栈之间来回传输。
- 将一个局部变量加载到操作栈:iload、iload_<n>、lload、lload_<n>、aload、aload_<n>
- 将一个数值从操作数栈存储到局部变量表:istore、istore_<n>、lstore、lstore_<n>、astore、astore_<n>
- 将一个常量加载到操作数栈:bipush、sipush、aconst_null、iconst_<i>、ldc
- 扩充局部变量表的访问索引的指令:wide
<n>:如iload_2,指从局部变量表下标2处整型元素加载到操作数栈顶。iload_0和iload语义一致
<i>:如iconst_2,指从整型常量池中常量2加载到操作数栈顶
bipush:将byte整型常量值推入操作数栈顶
sipush:将short整型常量值推入操作数栈顶
ldc:将int、float、String型常量值从常量池中推入至栈顶
运算指令
由于Java虚拟机没有直接支持byte、short、char、boolean类型的算术指令,使用操作int类型指令替代。
- 加法:iadd、ladd
- 减法:isub、lsub
- 乘法:imul、lmul
- 除法:idiv、ldiv
- 求余:irem、lrem
- 取反:ineg、lneg
- 局部变量自增:iinc
类型转换指令
小范围转大范围无需显示转换
- int到long、float、double
- long到float、double
- float到double
大范围到小范围需使用指令
- i2b:将栈顶int型数值强制转换成byte型数值并将结果压入栈顶
- f2l:将栈顶float型数值强制转换成int型数值并将结果压入栈顶
- d2l:将栈顶double型数值强制转换成long型数值并将结果压入栈顶
对象创建与访问指令
- 创建类实例:new
- 创建数组:newarray
- 访问类字段(static字段)和实例字段(非static字段):getfield、putfield、getstatic、putstatic
- 把一个数组元素加载到操作数栈:iaload、aaload、daload
- 把一个操作数栈的值存储到数组元素中:iastore、aastore、daload
- 取数组长度:arraylength
操作数栈管理指令
- 将操作数栈的栈顶一个或两个元素出栈:pop、pop2
- 复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶:dup、dup2
- 将栈顶的两个数值互换:swap
控制转移指令
控制转移指令就是在有条件或无条件地修改PC寄存器的值
- 条件分支:ifeq、iflt、、ifle、ifgt、ifge、ifnull、ifnonnnull
- 复合条件分支:tableswitch、lookupswitch
方法调用和返回指令
- invokevirtual:调用对象方法
- invokeinterface:调用接口方法,运行时搜索一个实现了这个接口方法的对象,找出适合的方法进行调用
- invokespecial:调用需要特殊处理的方法,如构造器、私有方法、父类方法
- invokestatic:调用类方法(static方法)
异常处理指令
采用异常表实现
- athrow:将栈顶异常抛出
同步指令
方法级同步是隐式的,访问标志ACC_SYNCHRONIZED。同步一段代码是由Java虚拟机指令集monitorenter和monitorexit来支持synchronized关键字语义。
示例代码
public class ByteCodeDemo {
public static void main(String[] args) {
ByteCodeDemo demo = new ByteCodeDemo();
demo.sayHello();
}
public void sayHello() {
synchronized (this) {
System.out.println("hello");
}
}
public synchronized void sayGoodBye() {
System.out.println("good bye");
}
}
使用javap -verbose ByteCodeDemo.class
解析字节码。对于私有方法不解析
public void sayHello();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #6 // String hello
9: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_1
13: monitorexit
14: goto 22
17: astore_2 // 存储异常信息到局部变量表
18: aload_1
19: monitorexit
20: aload_2 // 加载异常信息
21: athrow // 将栈顶异常抛出
22: return
Exception table:
from to target type
4 14 17 any // 4-14行指令抛出任何异常就执行17行指令
17 20 17 any // 17-20行指令抛出任何异常就执行17行指令
public synchronized void sayGoodBye();
descriptor: ()V
flags: ACC_PUBLIC, ACC_SYNCHRONIZED
Code:
stack=2, locals=1, args_size=1
0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #8 // String good bye
5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
网友评论