深入浅出JVM之字节码执行

1.javap反汇编工具

javap是jdk自带的反汇编工具,可以通过该命令查看编译后的class文件的常量池、字段以及方法等信息。

对于下面的这个类:

public class CalcCirc {
	public int doCalc() {
		int a=10;
		int b=20;
		int c=2;
		return (a+b)*c;
	}
}

使用javap -v CalcCirc.class可以看到该类的反汇编信息如下:

  public int doCalc();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: bipush        10
         2: istore_1
         3: bipush        20
         5: istore_2
         6: iconst_2
         7: istore_3
         8: iload_1
         9: iload_2
        10: iadd
        11: iload_3
        12: imul
        13: ireturn
.....//这里省去了LineNumberTable以及LocalVariableTable

stack=2表示在操作数栈的深度为2,这个一般是在编译的时候确定的,与方法中单一语句的执行复杂度有关,如果这条语句比较复杂,那么statck的数值就会增大。

locals=4表示有4个局部变量,源代码定义了a、b、c三个,再加上this变量,一共是4个局部变量。

args_size=1表示参数的个数,默认是1个参数,如果在方法中增加其他的参数则该数量会增加。

2.简单的字节码执行过程

字节码的执行过程需要关注的有以下内容:程序计数器、局部变量表、操作数栈。程序计数器用来指定程序当前运行的指针,也就是字节码的偏移量,对应的是字节码操作符前面的数字。局部变量表用来保存局部变量的最终结果,这里有4个局部变量,那么局部变量表的大小为4。操作数栈是保存执行过程中的实际内容,可以是局部变量的值,也可以是对象的引用。

执行第一条语句:0: bipush 10,程序计数器指向偏移量0,局部变量表中有this变量,将数字10压入操作数栈,执行后的结果如下:

程序计数器0
局部变量表0this
1
2
3
操作数栈
10

执行第二条语句:2: istore_1,将栈顶中的数值赋值给变量1:

程序计数器2
局部变量表0this
110
2
3
操作数栈

执行第三条语句:3: bipush 20,将数值20压入栈中:

程序计数器3
局部变量表0this
110
2
3
操作数栈
20

执行第四条语句:5: istore_2,将栈顶中元素赋值给局部变量2:

程序计数器5
局部变量表0this
110
220
3
操作数栈

执行第五条语句:6: iconst_2,将常量2压入栈中, 当int取值**-15**采用iconst指令,取值**-128127采用bipush指令,取值-3276832767**采用sipush指令,取值**-21474836482147483647**采用 ldc 指令 :

程序计数器6
局部变量表0this
110
220
3
操作数栈
2

执行第六条语句:7: istore_3,将栈顶中元素赋值给局部变量3:

程序计数器5
局部变量表0this
110
220
32
操作数栈

后面的语句依次是加载局部变量1和局部变量2,iadd是从栈顶中取两个元素做加法,然后将结果放入栈中,然后加载局部变量3,imul从栈中获取两个元素做乘法,并将结果返回栈中,ireturn是将栈顶元素返回,这个返回值作为函数的返回值。

3.常用的字节码

字节码指令为一个byte整数,不同的整数代表不同的执行命令,常用的字节码指令如下:

常量入栈:

局部变量入栈:
xload_n(n为0 1 2 3):x分别表示int,long,float,double,object ref,n表示局部变量下表
xaload(x为i l f d a b c s):x分别表示int, long, float, double, obj ref ,byte,char,short,该指令将从数组中取得给定索引的值,将该值压栈。比如iaload,执行前,栈:…, arrayref, index,它取得arrayref所在数组的index的值,并将值压栈。执行后,栈:…, value

出栈装载入局部变量:
xstore_n(x为i l f d a,n为 0 1 2 3):将栈顶元素出栈,存入第n个局部变量
xastore(x为i l f d a b c s):将值存入数组中,iastore
执行前,栈:…,arrayref, index, value
执行后,栈:…
将value存入arrayref[index]

通用操作:nop, pop:弹出栈顶元素,dup:复制栈顶元素,复制的内容压入栈中

类型转化:i2l,i2f,d2l:将int转化为long,int转化为float,double转化为long

整数运算:iadd:int相加

浮点运算:fadd:float相加

对象操作:new:新建一个对象,getfield:获取一个对象字段,getstatic:获取静态变量

条件控制:ifeq:如果是0,则跳转,比较栈顶元素是否为0,如果是0则跳转到指定语句。

方法调用:

invokevirtual:调用实例方法,它是动态分配的调用指令,引用的类型并不能决定方法属于哪个类型,需要在实际运行的时候才能确定。
invokespecial: 调用实例初始化,父类初始化和私有方法。
invokestatic: 调用一个类(static)方法。
invokeinterface:调用类的接口方法
xreturn(x为 i l f d a 或为空):返回值,a为返回一个对象的引用

4.使用ASM生成Java字节码

ASM是java字节码操作框架,可以用于修改现有类或者动态生成新类,比如spring,AspectJ等都使用了asm框架。cglib对ASM做了一层封装,其他的框架会使用cglib来对类进行操作。

5.JIT及其相关的参数

字节码执行性能较差,对于热点代码编译成机器码再执行,在运行时的编译,叫做JIT:Just-In-Time。

所谓的热点代码是只调用的方法,或者被多次调用的循环体。jvm里会有方法调用计数器(方法调用次数)和回边计数器(方法内循环次数),通过判断这些计数器的数值是否超过阈值来判断是否将热点代码编译成机器码来执行。

-XX:CompileThreshold=1000:设置计数器的阈值
-XX:+PrintCompilation:打印出已经编译成机器码的方法

附:

java 字节码 指令集 汇编

常量入栈指令
操作码(助记符)操作数描述(栈指操作数栈)
aconst_nullnull值入栈。
iconst_m1-1(int)值入栈。
iconst_00(int)值入栈。
iconst_11(int)值入栈。
iconst_22(int)值入栈。
iconst_33(int)值入栈。
iconst_44(int)值入栈。
iconst_55(int)值入栈。
lconst_00(long)值入栈。
lconst_11(long)值入栈。
fconst_00(float)值入栈。
fconst_11(float)值入栈。
fconst_22(float)值入栈。
dconst_00(double)值入栈。
dconst_11(double)值入栈。
bipushvaluebytevaluebyte值带符号扩展成int值入栈。
sipushvaluebyte1valuebyte2(valuebyte1 << 8) | valuebyte2 值带符号扩展成int值入栈。
ldcindexbyte1常量池中的常量值(int, float, string reference, object reference)入栈。
ldc_windexbyte1indexbyte2常量池中常量(int, float, string reference, object reference)入栈。
ldc2_windexbyte1indexbyte2常量池中常量(long, double)入栈。
局部变量值转载到栈中指令
操作码(助记符)操作数描述(栈指操作数栈)
(wide)aloadindexbyte从局部变量indexbyte中装载引用类型值入栈。
aload_0从局部变量0中装载引用类型值入栈。
aload_1从局部变量1中装载引用类型值入栈。
aload_2从局部变量2中装载引用类型值入栈。
aload_3从局部变量3中装载引用类型值入栈。
(wide)iloadindexbyte从局部变量indexbyte中装载int类型值入栈。
iload_0从局部变量0中装载int类型值入栈。
iload_1从局部变量1中装载int类型值入栈。
iload_2从局部变量2中装载int类型值入栈。
iload_3从局部变量3中装载int类型值入栈。
(wide)lloadindexbyte从局部变量indexbyte中装载long类型值入栈。
lload_0从局部变量0中装载int类型值入栈。
lload_1从局部变量1中装载int类型值入栈。
lload_2从局部变量2中装载int类型值入栈。
lload_3从局部变量3中装载int类型值入栈。
(wide)floadindexbyte从局部变量indexbyte中装载float类型值入栈。
fload_0从局部变量0中装载float类型值入栈。
fload_1从局部变量1中装载float类型值入栈。
fload_2从局部变量2中装载float类型值入栈。
fload_3从局部变量3中装载float类型值入栈。
(wide)dloadindexbyte从局部变量indexbyte中装载double类型值入栈。
dload_0从局部变量0中装载double类型值入栈。
dload_1从局部变量1中装载double类型值入栈。
dload_2从局部变量2中装载double类型值入栈。
dload_3从局部变量3中装载double类型值入栈。
aaload从引用类型数组中装载指定项的值。
iaload从int类型数组中装载指定项的值。
laload从long类型数组中装载指定项的值。
faload从float类型数组中装载指定项的值。
daload从double类型数组中装载指定项的值。
baload从boolean类型数组或byte类型数组中装载指定项的值(先转换为int类型值,后压栈)。
caload从char类型数组中装载指定项的值(先转换为int类型值,后压栈)。
saload从short类型数组中装载指定项的值(先转换为int类型值,后压栈)。
将栈顶值保存到局部变量中指令
操作码(助记符)操作数描述(栈指操作数栈)
(wide)astoreindexbyte将栈顶引用类型值保存到局部变量indexbyte中。
astroe_0将栈顶引用类型值保存到局部变量0中。
astore_1将栈顶引用类型值保存到局部变量1中。
astore_2将栈顶引用类型值保存到局部变量2中。
astore_3将栈顶引用类型值保存到局部变量3中。
(wide)istoreindexbyte将栈顶int类型值保存到局部变量indexbyte中。
istore_0将栈顶int类型值保存到局部变量0中。
istore_1将栈顶int类型值保存到局部变量1中。
istore_2将栈顶int类型值保存到局部变量2中。
istore_3将栈顶int类型值保存到局部变量3中。
(wide)lstoreindexbyte将栈顶long类型值保存到局部变量indexbyte中。
lstore_0将栈顶long类型值保存到局部变量0中。
lstore_1将栈顶long类型值保存到局部变量1中。
lstore_2将栈顶long类型值保存到局部变量2中。
lstroe_3将栈顶long类型值保存到局部变量3中。
(wide)fstoreindexbyte将栈顶float类型值保存到局部变量indexbyte中。
fstore_0将栈顶float类型值保存到局部变量0中。
fstore_1将栈顶float类型值保存到局部变量1中。
fstore_2将栈顶float类型值保存到局部变量2中。
fstore_3将栈顶float类型值保存到局部变量3中。
(wide)dstoreindexbyte将栈顶double类型值保存到局部变量indexbyte中。
dstore_0将栈顶double类型值保存到局部变量0中。
dstore_1将栈顶double类型值保存到局部变量1中。
dstore_2将栈顶double类型值保存到局部变量2中。
dstore_3将栈顶double类型值保存到局部变量3中。
aastore将栈顶引用类型值保存到指定引用类型数组的指定项。
iastore将栈顶int类型值保存到指定int类型数组的指定项。
lastore将栈顶long类型值保存到指定long类型数组的指定项。
fastore将栈顶float类型值保存到指定float类型数组的指定项。
dastore将栈顶double类型值保存到指定double类型数组的指定项。
bastroe将栈顶boolean类型值或byte类型值保存到指定boolean类型数组或byte类型数组的指定项。
castore将栈顶char类型值保存到指定char类型数组的指定项。
sastore将栈顶short类型值保存到指定short类型数组的指定项。
wide****指令
操作码(助记符)操作数描述(栈指操作数栈)
wide使用附加字节扩展局部变量索引(iinc指令特殊)。
通用(无类型)栈操作指令
操作码(助记符)操作数描述(栈指操作数栈)
nop空操作。
pop从栈顶弹出一个字长的数据。
pop2从栈顶弹出两个字长的数据。
dup复制栈顶一个字长的数据,将复制后的数据压栈。
dup_x1复制栈顶一个字长的数据,弹出栈顶两个字长数据,先将复制后的数据压栈,再将弹出的两个字长数据压栈。
dup_x2复制栈顶一个字长的数据,弹出栈顶三个字长的数据,将复制后的数据压栈,再将弹出的三个字长的数据压栈。
dup2复制栈顶两个字长的数据,将复制后的两个字长的数据压栈。
dup2_x1复制栈顶两个字长的数据,弹出栈顶三个字长的数据,将复制后的两个字长的数据压栈,再将弹出的三个字长的数据压栈。
dup2_x2复制栈顶两个字长的数据,弹出栈顶四个字长的数据,将复制后的两个字长的数据压栈,再将弹出的四个字长的数据压栈。
swap交换栈顶两个字长的数据的位置。Java指令中没有提供以两个字长为单位的交换指令。
类型转换指令
操作码(助记符)操作数描述(栈指操作数栈)
i2f将栈顶int类型值转换为float类型值。
i2l将栈顶int类型值转换为long类型值。
i2d将栈顶int类型值转换为double类型值。
f2i将栈顶float类型值转换为int类型值。
f2l将栈顶float类型值转换为long类型值。
f2d将栈顶float类型值转换为double类型值。
l2i将栈顶long类型值转换为int类型值。
l2f将栈顶long类型值转换为float类型值。
l2d将栈顶long类型值转换double类型值。
d2i将栈顶double类型值转换为int类型值。
d2f将栈顶double类型值转换为float类型值。
d2l将栈顶double类型值转换为long类型值。
i2b将栈顶int类型值截断成byte类型,后带符号扩展成int类型值入栈。
i2c将栈顶int类型值截断成char类型值,后带符号扩展成int类型值入栈。
i2s将栈顶int类型值截断成short类型值,后带符号扩展成int类型值入栈。
整数运算
操作码(助记符)操作数描述(栈指操作数栈)
iadd将栈顶两int类型数相加,结果入栈。
isub将栈顶两int类型数相减,结果入栈。
imul将栈顶两int类型数相乘,结果入栈。
idiv将栈顶两int类型数相除,结果入栈。
irem将栈顶两int类型数取模,结果入栈。
ineg将栈顶int类型值取负,结果入栈。
ladd将栈顶两long类型数相加,结果入栈。
lsub将栈顶两long类型数相减,结果入栈。
lmul将栈顶两long类型数相乘,结果入栈。
ldiv将栈顶两long类型数相除,结果入栈。
lrem将栈顶两long类型数取模,结果入栈。
lneg将栈顶long类型值取负,结果入栈。
(wide)iincindexbyteconstbyte将整数值constbyte加到indexbyte指
** 浮点运算**
操作码(助记符)操作数描述(栈指操作数栈)
fadd将栈顶两float类型数相加,结果入栈。
fsub将栈顶两float类型数相减,结果入栈。
fmul将栈顶两float类型数相乘,结果入栈。
fdiv将栈顶两float类型数相除,结果入栈。
frem将栈顶两float类型数取模,结果入栈。
fneg将栈顶float类型值取反,结果入栈。
dadd将栈顶两double类型数相加,结果入栈。
dsub将栈顶两double类型数相减,结果入栈。
dmul将栈顶两double类型数相乘,结果入栈。
ddiv将栈顶两double类型数相除,结果入栈。
drem将栈顶两double类型数取模,结果入栈。
dneg将栈顶double类型值取负,结果入栈。
逻辑运算——移位运算
操作码(助记符)操作数描述(栈指操作数栈)
ishl左移int类型值。
lshl左移long类型值。
ishr算术右移int类型值。
lshr算术右移long类型值。
iushr逻辑右移int类型值。
lushr逻辑右移long类型值。
逻辑运算——按位布尔运算
操作码(助记符)操作数描述(栈指操作数栈)
iand对int类型按位与运算。
land对long类型的按位与运算。
ior对int类型的按位或运算。
lor对long类型的按位或运算。
ixor对int类型的按位异或运算。
lxor对long类型的按位异或运算。
控制流指令——条件跳转指令
操作码(助记符)操作数描述(栈指操作数栈)
ifeqbranchbyte1
branchbyte2
若栈顶int类型值为0则跳转。
ifnebranchbyte1
branchbyte2
若栈顶int类型值不为0则跳转。
ifltbranchbyte1
branchbyte2
若栈顶int类型值小于0则跳转。
iflebranchbyte1
branchbyte2
若栈顶int类型值小于等于0则跳转。
ifgtbranchbyte1
branchbyte2
若栈顶int类型值大于0则跳转。
ifgebranchbyte1
branchbyte2
若栈顶int类型值大于等于0则跳转。
if_icmpeqbranchbyte1
branchbyte2
若栈顶两int类型值相等则跳转。
if_icmpnebranchbyte1
branchbyte2
若栈顶两int类型值不相等则跳转。
if_icmpltbranchbyte1
branchbyte2
若栈顶两int类型值前小于后则跳转。
if_icmplebranchbyte1
branchbyte2
若栈顶两int类型值前小于等于后则跳转。
if_icmpgtbranchbyte1
branchbyte2
若栈顶两int类型值前大于后则跳转。
if_icmpgebranchbyte1
branchbyte2
若栈顶两int类型值前大于等于后则跳转。
ifnullbranchbyte1
branchbyte2
若栈顶引用值为null则跳转。
ifnonnullbranchbyte1
branchbyte2
若栈顶引用值不为null则跳转。
if_acmpeqbranchbyte1
branchbyte2
若栈顶两引用类型值相等则跳转。
if_acmpnebranchbyte1
branchbyte2
若栈顶两引用类型值不相等则跳转。
控制流指令——比较指令
操作码(助记符)操作数描述(栈指操作数栈)
lcmp比较栈顶两long类型值,前者大,1入栈;相等,0入栈;后者大,-1入栈。
fcmpl比较栈顶两float类型值,前者大,1入栈;相等,0入栈;后者大,-1入栈;有NaN存在,-1入栈。
fcmpg比较栈顶两float类型值,前者大,1入栈;相等,0入栈;后者大,-1入栈;有NaN存在,-1入栈。
dcmpl比较栈顶两double类型值,前者大,1入栈;相等,0入栈;后者大,-1入栈;有NaN存在,-1入栈。
dcmpg比较栈顶两double类型值,前者大,1入栈;相等,0入栈;后者大,-1入栈;有NaN存在,-1入栈。
控制流指令——无条件跳转指令
操作码(助记符)操作数描述(栈指操作数栈)
gotobranchbyte1
branchbyte2
无条件跳转到指定位置。
goto_wbranchbyte1
branchbyte2
branchbyte3
branchbyte4
无条件跳转到指定位置(宽索引)。
** 控制流指令——表跳转指令**
操作码(助记符)操作数描述(栈指操作数栈)
tableswitch<0-3bytepad>
defaultbyte1
defaultbyte2
defaultbyte3
defaultbyte4
lowbyte1
lowbyte2
lowbyte3
lowbyte4
highbyte1
highbyte2
highbyte3
highbyte4
jump offsets…
通过索引访问跳转表,并跳转。
lookupswitch<0-3bytepad>
defaultbyte1
defaultbyte2
defaultbyte3
defaultbyte4
npairs1
npairs2
npairs3
npairs4
match offsets
通过键值访问跳转表,并跳转。
控制流指令——异常和finally
操作码(助记符)操作数描述(栈指操作数栈)
athrow抛出异常。
jsrbranchbyte1
branchbyte2
跳转到子例程序。
jsr_wbranchbyte1
branchbyte2
branchbyte3
branchbyte4
跳转到子例程序(宽索引)。
(wide)retindexbyte返回子例程序。
对象操作指令
操作码(助记符)操作数描述(栈指操作数栈)
newindexbyte1
indexbyte2
创建新的对象实例。
checkcastindexbyte1
indexbyte
类型强转。
instanceofindexbyte1
indexbyte2
判断类型。
getfieldindexbyte1
indexbyte2
获取对象字段的值。
putfieldindexbyte1
indexbyte2
给对象字段赋值。
getstaticindexbyte1
indexbyte2
获取静态字段的值。
putstaticindexbyte1
indexbyte2
给静态字段赋值。
数组操作指令
操作码(助记符)操作数描述(栈指操作数栈)
newarrayatype创建type类型的数组。
anewarrayindexbyte1
indexbyte2
创建引用类型的数组。
arraylength获取一维数组的长度。
multianewarrayindexbyte1
indexbyte2
dimension
创建dimension维度的数组。
方法调用指令
操作码(助记符)操作数描述(栈指操作数栈)
invokespecialindexbyte1
indexbyte2
编译时方法绑定调用方法。
invokevirtualindexbyte1
indexbyte2
运行时方法绑定调用方法。
invokestaticindexbyte1
indexbyte2
调用静态方法。
invokeinterfaceindexbyte1
indexbyte2
count0
调用接口方法。
方法返回指令
操作码(助记符)操作数描述(栈指操作数栈)
ireturn返回int类型值。
lreturn返回long类型值。
freturn返回float类型值。
dreturn返回double类型值。
areturn返回引用类型值。
returnvoid函数返回。
线程同步指令
操作码(助记符)操作数描述(栈指操作数栈)
monitorenter进入并获得对象监视器。
monitorexit释放并退出对象监视器。

参考原文链接: http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值