先来看看什么是字节码指令
上图的Code区域即为字节码指令。
JVM的指令是由一个字节长度的、代表某种操作的数字(例如iconst_0,iconst_0前面的数据表示语句的顺序,可用于条件判断语句)以及跟在其后的0到多个操作数(或是操作数的索引)构成的(上图指令没有操作数),大多数指令都包含了其操作所对应的数据类型的信息,例如iload指令将int型数据从局部变量表加载到操作数栈中,由于JVM指令只有一个字节,这意味着无法让每种指令都支持所有数据类型,所以一些指令在必要的时候会将一些不支持的类型转换成支持的类型,在将char、byte、short型数据放进局部变量表或操作数栈时会转换成int型数据,可能就是为了方便一些指令进行操作。
一般情况下指令中的首字母由以下含义:i—int,l—long,s—short,b—byte,c—char,f—float,d—double,a—reference(引用)
JVM的指令集大致分为以下几种:
将一个数据从局部变量表加载到操作数栈:
iload、lload、fload、dload、aload,其后接的是操作数的局部变量表索引,可以带wide进行扩展,例如iload的指令格式为iload index,带wide:wide iload index1 index2,此时操作数下标为(index<<8)| index8
iload_n、lload_n、fload_n、dload_n、aload_n(n=0、1、2、3),代表将索引为n的局部变量表的内容压入操作数栈,由于少了操作数(直接在指令中包含),所以执行速度较快。
将一个数据从操作数栈加载到局部变量表:
istore、lstore、fstore、dstore、astore,可以带wide扩展,其后接的数值为存储数据的局部变量表的索引。
istore_n、lstore_n、fstore_n、dstore_n、astore_n(n=0、1、2、3),代表将操作数存储到索引为n的局部变量表中。
将一个常量加载到操作数栈中:
iconst_m1:int型常量-1进栈。
iconst_n:int型常量n进栈(n=0、1、2、3、4、5)
lconst_n:long型常量值n进栈(n=0、1)
fconst_n:float型常量值n进栈(n=0、1、2)
dconst_n:double型常量值n进栈(n=0、1)
以下指令后接的均为操作数在常量池中的下标,常量池的内存空间类似于数组
bipush:byte型常量进栈
sipush:short型常量进栈
ldc:将int、float、String型常量从常量池中推进栈
ldc_w:将int、float、String型常量从常量池中推进栈,为宽索引(意味着在常量池中,该数据较大,一个字节存储不了)
ldc2_w:将long、double型常量从常量池中推进栈,为宽索引
aconst_null:null进栈
将一个数组元素压进栈中(应该是从堆中取出元素)
iaload:int型数组的指定下标处的值进栈。
laload:long型数组的指定下标处的值进栈。
faload:float型数组的指定下标处的值进栈。
daload:double型数组的指定下标处的值进栈。
aaload:栈顶的数组索引、数组引用先后出栈,并根据这两个值取出对应的数组元素值进栈,若引用为null或是索引大于数组大小,会抛出异常。从栈中取出的参数将作为index(指定下标),arrayref(指定数组引用)的值
将一个操作数从栈中存入到堆中的数组的对应索引处:
iastore:将栈顶int型数据存入指定的数组下标处。
lastore:将栈顶long型数据存入指定的数组下标处。
fastore:将栈顶float型数据存入指定的数组下标处。
dastore:将double型数据存入指定的数组下标处。
aastore:有三个参数index(int型数据,指定下标)、arrayref(对数组的引用)、value(引用类型),从栈中退出三个量,分别作为这三个变量的值,该指令主要用来将引用类型数据存入引用数组中。
bastore:将boolean或是byte型数据存入指定数组下标处
castore:将char型数据存入指定数组下标处。
sastore:将short型数据存入指定数组下标处。
栈相关的基本操作
pop:栈顶数值出栈(准确来说应该是弹出一个存储单元),该数值类型不能是long或是double型(由于栈中的一个存储单元占用32位,而这两者为64位的数据)
pop2:弹出栈顶的两个存储单元,若不是long和double类型数据,则有两个数据。
dup:复制栈顶数值,并将复制值进栈。
dup_x1:复制栈顶数值,并且将复制值进栈两次。
dup_x2:复制栈顶数值,并且将复制值压进栈两次或是三次。
dup2:复制栈顶两个存储单元的值,并将复制值压进栈。
dup2_x1:复制栈顶两个存储单元的值,并且将复制值压进栈两次。
dup2_x2:复制栈顶两个存储单元的值,并将复制值压进栈两次或是三次。
算数运算操作
swap:栈顶的两个数值交换(要求不能是long、double型)
(i、l、f、d)add:栈顶两个数据相加,并将结果压入栈
(i、l、f、g)sub:栈顶两个数据相减,并将结果压入栈
(i、l、f、d)mul:栈顶两数据相乘,并将结果压入栈
(i、l、f、d)div:栈顶两数据相除,并将结果压入栈
(i、l、f、d)rem:栈顶两数据取模,并将结果压入栈
(i、l、f、d)neg:栈顶数据取反,并将结果压入栈
(i、l)shl:将数据按对应位数左移,并将结果压入栈
(i、l)shr:将数据按对应位数右移,并将结果压入栈
(i、l)ushr:将无符号int或是long数据右移指定位数,并将结果压入栈
(i、l)and:栈顶两个数据按位与,并将结果压入栈
(i、l)or:栈顶两个数据按位或,并将结果压入栈
(i、l)xor:栈顶两个数据按位异或,并将结果压入栈
iinc:指定int型变量增加指定值
类型转换:
i2(l、f、b、d、c、s):将栈顶的int型数据转换成对应类型并将结果压入栈,其中的b表示byte类型
l2(i、f、d):将栈顶的long型数据转换成对应类型并将结果压入栈。
f2(i、l、d):将栈顶的float型数据转换成对应类型并将结果压入栈。
d2(i、l、f):将栈顶的double型数据转换成对应类型并将结果压入栈。
比较操作:fcmpl:比较栈顶两个float型数据大小,并将结果-1、0、1压入栈内,当其中一个值为NaN(not a number,表示不明确的数值结果)时,将-1压入栈
fcmpg:比较栈顶两个float型数据大小,并将结果-1、0、1压入栈内,当其中一个值为NaN时,将1压入栈。
dcmpl:比较栈顶两个double型数据大小,并将结果-1、0、1压入栈内,当其中一个值为NaN时,将-1压入栈。
dcmpg:比较栈顶两个double型数据大小,并将结果-1、0、1压入栈内,当其中一个值为NaN时,将1压入栈。
条件判断语句
以下语句后接跳转到的语句的位置
iflt:当栈顶int型数值小于0时跳转。
ifge:当栈顶int型数值大于等于0时跳转。
ifgt:当栈顶int型数值大于0时跳转。
ifle:当栈顶int型数值小于等于0时跳转。
if_icmpeq:比较栈顶两个int型数值的大小,当结果等于0时跳转。
if_icmpne:比较栈顶两个int型数值的大小,当结果不等于0时跳转。
if_icmplt:比较栈顶两个int型数值的大小,当结果小于0时跳转。
if_icmpge:比较栈顶两个int型数据的大小,当结果大于等于0时跳转。
if_icmple:比较栈顶两个int型数据的大小,当结果大于0时跳转。
if_acmpeq:比较栈顶两引用型数据的大小,当结果相等时跳转。(应该是判断两者指向的实例对象是否相等)
if_acmpne:比较栈顶两引用型数据的大小,当结果不相等时跳转。
goto:无条件跳转。
goto_w:无条件跳转(宽索引)。
ifnull:为null时跳转。
ifnonnull:不为null时跳转。
在java SE 7或是更高版本中,jsr、ret、jsr_w、ret_w已经禁用。
tableswitch:用于switch条件跳转,case值是连续的情况,(可变长度指令)
lookupswitch:用于switch条件跳转,case值不连续的情况,(可变长度指令)
返回语句:
(i、l、f、d、a)return:从当前方法返回对应类型的数据(a表示引用类型)
return:从当前方法返回void
类相关操作:
getstatic:获取指定类的静态域,并将结果压入栈,其后接一个符号引用
putstatic:为指定的类的静态域赋值。
getfield:获取指定类的实例域,并将结果压入栈。
putfield:给指定类的实例域赋值。
invokevirtual:调用实例方法。
invokespecial:调用父类的构造方法、实例初始化方法、私有方法。
invokestatic:调用静态方法。
invokeinterface:调用接口方法。
new:创建一个实例对象,并将引用值压入栈顶(我个人的理解是只是申请了内存空间,但具体调用什么构造器由接下来的invokevirtual来决定)。
newarray:创建一个基本数据类型的数组,并将其引用值压入栈顶。
arraylength:获取数组长度并压入值并压入栈顶。
instanceof:检验对象是否是指定类的实例,将结果压入栈,1表示是,0表示不是。
monitorenter:获取对象的锁,用于同步方法或同步块(多线程设计中用到)。
multianewarrary:创建指定类型和指定维度的多维数组(执行该指令时,操作数栈中必须包含各维度的长度值),并将引用值压入栈。
异常检测
athrow:将栈顶异常抛出。
checkcast:检验类型转换,检验未通过则抛出ClassCastException。
关于栈的相关操作,除了指明复制外,其余均会从栈中取出数据(不是复制出数据),将局部变量表中的数据压入操作数栈压入的是复制值。