基本指令分析:
规律:
store表示弹出操作数栈(操作数栈是一个栈)顶的数据放入局部变量区
store_x表示弹出操作数栈顶的数据放入局部变量区索引为x的地方
load表示将局部变量区中某个位置(即某个索引,因为局部变量区是一个数组)的局部变量压入操作数栈
load_x表示将局部变量区中x位置的局部变量压入操作数栈
astore表示弹出操作数栈顶的对象引用,并放入局部变量区
astore_x也跟前面有相同的规则
aload表示将局部变量区中某个位置的对象引用压入操作数栈
aload_x也跟前面有相同的规则
const_x表示将某个值(x)压入操作数栈
bipush x表示将某个值(x,类型是byte)转换为int类型压入操作数栈
sipush x 表示将某个值(x,类型为short)转换为int类型压入操作数栈
ldc x表示将常量池中的某个入口地址(x表示常量池入口地址)压入操作数栈
pop表示将操作数栈顶部的数据弹出栈
dup表示复制操作数栈顶的数据
下面,让我们开始通过一些例子来分析这些指令:
jClassLib是一个开源的分析类文件内容的工具,可以从网上下载,运行之后,类似下面的界面:
比如下面的代码:
public class HelloWorld01 {
/** * @param args */ public static void main(String[] args) { int i0 = 1; int i1 = 10; int i2 = i0 + i1; System.out.println(i2); } } |
对应的方法的指令如下:
int i0=1,目标是给变量i0赋值:
iconst_1意思是将1这个值压入操作数栈
istore_1意思是将操作数栈顶的数据(即刚压进去的值1)弹出并存储在变量区中索引为1的地方
bipush 10意思是将10压入操作数栈
istore_2意思是将操作数栈顶的数据(即刚压进去的10)弹出并存储在变量区中索引为2的地方
iload_1表示将变量区中索引为1的值压入操作数栈(这个值就是1)
iload_2表示将变量区中索引为2的值压入操作数栈(这个值就是10)
iadd表示将操作数栈中的两个数弹出相加,并将结果压入操作数栈中
istore_3表示将操作数栈顶的数据(即在iadd操作码中压进去的相加之后的结果)弹出并存储在变量区中索引为3的地方
从上面的分析可以看到,很多数据,比如1、10这些常量值,编译器是直接将它们放在指令序列中!
再看下面的代码:
public class HelloWorld02 {
/** * @param args */ public static void main(String[] args) { long i0 = 100; long i1 = 999999999L; long i2 = i0 + i1; System.out.println(i2); } } |
其对应的指令序列为:
ldc2_w #16表示将常量池16号的值压入操作数栈,两个字长(一个字长是32位)的宽度
lstore_1表示将操作数栈顶的数据弹出放到变量区索引号为1的地方
ldc2_w #18表示将常量池18号的值压入操作数栈
lstore_3表示将操作数栈顶的数据弹出放到变量区索引号为3的地方(之所以放到索引号为3而不是2的地方,是因为一个Long占据了两个字长,即两个索引号)
lload_1和lload_3自然也就表示将变量区索引号为1和3的两个值压入操作数栈
ladd表示将操作数栈中的两个数据弹出并相加,结果再次入栈
lstore 5表示将操作数栈顶的数据(刚才相加的计算结果)弹出,并放到变量区索引号为5的地方
从上面的分析中,可以知道,对于比较大的数据,编译器会把这些数据放到常量池中,在指令序列中则仅指明位置而已。
再看下面的例子:
public class HelloWorld03 {
/** * @param args */ public static void main(String[] args) { String stringvalue1 = "H"; String stringvlaue2 = "H"; String stringvalue3 = "Hello"; char c = 'H'; } } |
上面例子可以看出,字符串也是放到常量池中的,而且相同的字符串在常量池中只有一份。
字符则把它当成是一个int类型压入到操作数栈中。
再看下面的代码:
public class HelloWorld05 {
/** * @param args */ public static void main(String[] args) { int result = 0; for(int i=0; i<10; i++){ result = result + i; } } } |
iconst_0表示将0压入操作数栈
istore_1表示将栈顶数据弹出存放到变量区,索引号为1(即给result这个变量赋值)
iconst_0表示将0压入操作数栈
istore_2表示将栈顶数据弹出存放到变量区,索引号为2(即给i这个变量赋值,int i=0)
goto 14,跑到14行去
在14行中,iload_2表示把变量区索引号为2的值压入操作数栈(即取出i变量)
bipush 10,表示把10这个值压入操作数栈
if_icmplt 7,表示弹出操作数栈的两个数据,并判断操作数栈中的两个数的大小(即是否i<10?),如果是,则跑到第7行,否则,直接往下执行了
在第7行中:iload_1即把变量区索引号为1的值压入操作数栈(即result变量)
iload_2表示把变量区索引号为2的值压入操作数栈(即i变量)
iadd前面已经讲过了,就是把两个操作数栈中的数据弹出并相加,把结果重新压入操作数栈
istore_1表示把操作数栈中的数据弹出,并存放到变量区索引号为1的地方(即更新了result变量的值)
iinc 2 by 1,表示的是将变量区索引号为2的值自增1
然后又到了14行,将重复上述的过程!