3 JVM常用指令
3.1. 指令集介绍
1)指令集的分类
指令集大体上可分为两类:基于栈的指令集、基于寄存器的指令集。基于栈的指令集是利用出栈和进栈来实现的,基于寄存器的指令集是通过寄存器的特性来实现的,基于寄存器的指令集复杂但是高效。
2)基于栈的指令集
大多数指令并不包含操作数,只有一个操作码,指令参数都存放于操作数栈中,操作码的长度为一个字节(即0-255),所以指令集的操作码总数不会超过256条。
3)样例介绍
【样例程序1】
程序代码、程序对应生成的字节码文件、局部变量表内容如下所示
因为这里的方法不是静态方法,所以在局部变量中的首位是this,其次是局部变量a、b和c。其中字节码文件的程序执行顺序是:执行iload_1和iload_2指令将局部变量中的第1号位置数据a和第2号索引数据b的数据压入栈,iadd将栈中的两个元素弹出并相加得到c,然后压入栈,弹出栈中数据c并保存在局部变量表的3号位置,返回。
【样例程序2】
根据下面三个例子,以最复杂的第三个为例介绍:红色框表示执行第一句话,其中new#2表示创建一个新的对象,对象放在栈中的2号索引处,对象的类为后面<>里面的内容。dup指令表示复制一份栈中的对象。Invokespecial表示使用该复制份对象完成初始化加载,加载完成之后将该复制份弹出栈。橘色框表示执行第二句代码,aload1表示加载局部变量表中索引1位置的数据h,invokevirtrual#4表示执行栈中第4索引位置对应的方法,istore2表示将栈中的结果保存到局部变量表的2号索引位置,结束。
【样例程序3】
递归方法的程序以及字节码文件如下所示,其中if_icmpne 7指令表示从栈中弹出两条指令并比较,如果不等则执行后面的第7条执行,如果相等则继续往下执行。
3.2. 常用指令
1)常用的指令如下所示:
指令 释义
iconst_1 int型常量值1进栈
bipush 将一个byte型常量值推送至栈顶
iload_1 第二位置的一个int型局部变量进栈,从0开始计数
istore_1 将栈顶int型数值存入第二位置,从0开始计数
iadd 栈顶两int型数值相加,并且结果进栈
return 当前方法返回void
getstatic 获取指定类的静态域,并将其值压入栈顶
putstatic 为指定类的静态域赋值
invokevirtual 调用实例方法,支持多态
invokespecial 可以直接调用的方法,不需要多态
例如,调用超类构造方法、实例初始化方法、私有方法
invokestatic 调用静态方法
invokeinterface 调用接口方法
invokeDynamic lambda表达式或者反射或者其他动态语言scala kotlin,或者CGLib ASM,动态产生的class,会用到的指令
new 创建一个对象,并且其引用进栈
newarray 创建一个基本类型数组,并且其引用进栈
2)特殊的指令
因为指令条数有限(最大为256条),不可能支持所有的数据类型,大部分指令没有支持byte、char、short、boolean类型,在编译器或者运行期时将这些类型转为int类型数据,然后通过int类型的相关指令去操作。