首先新建一个Hello.java文件,文件代码如下:
public class Hello{
public int foo(int a,int b){
return (a+b)*(a-b);
}
public static void main(String[] args){
Hello hello = new Hello();
System.out.println(hello.foo(5,3));
}
}
1、将Hello.java 编译成Hello.class,输入命令
javac Hello.java
2、使用javap命令反编译Hello.class查看foo()函数的java字节码
javap -c -classpath . Hello
输出结果
Compiled from "Hello.java"
public class Hello {
public Hello();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int foo(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: iload_1
4: iload_2
5: isub
6: imul
7: ireturn
public static void main(java.lang.String[]);
Code:
0: new #7 // class Hello
3: dup
4: invokespecial #9 // Method "<init>":()V
7: astore_1
8: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
11: aload_1
12: iconst_5
13: iconst_3
14: invokevirtual #16 // Method foo:(II)I
17: invokevirtual #20 // Method java/io/PrintStream.println:(I)V
20: return
}
查看foo()方法发现一共占用了8个字节,并且这些指令都没有参数。
指令iload_1 可以分成3个部分,i表示int,load表示将局部变量压栈。
下划线右边的数字代表取第几个局部变量,下标从0开始。
结合以上我们发现foo()函数前两条指令分别为iload_1,iload_2,这和下标从0开始好像有冲突,按照理解前两条指令应该是iload_0,iload_1
。笔者带着这个疑问习惯性的先去问度娘,最终没有找到想要的答案。
和同事讨论了一番之后觉得可能是对象的原因,接着试着将foo()方法改为static,再次编译看看结果
public static int foo(int, int);
Code:
0: iload_0
1: iload_1
2: iadd
3: iload_0
4: iload_1
5: isub
6: imul
7: ireturn
可以看到原本的iload_1变成了iload_0,为什么会这样呢 ,我们接着将static去掉然后打印this再次编译
public int foo(int, int);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
7: iload_1
8: iload_2
9: iadd
10: iload_1
11: iload_2
12: isub
13: imul
14: ireturn
我们看到第二行指令有个aload_0(从局部变量中装载引用类型值),
从指令上看对象方法的第一个局部变量为this,这里就很好的解释了为什么会从索引为1开始了。
上面的疑问基本解决了,我们再解释下foo()方法中指令的意思,我们用以下指令来讲解
public int foo(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: iload_1
4: iload_2
5: isub
6: imul
7: ireturn
指令1:将第一个参数的int类型的局部变量进行压栈
指令2:将第二个参数的int类型的局部变量进行压栈
指令3:从栈顶弹出两个int类型的变量做加法再将结果(标记为R1)压栈
这个时候栈里应该只有R1
指令4:将第一个参数的int类型的局部变量进行压栈
指令5:将第二个参数的int类型的局部变量进行压栈
指令6:从栈顶弹出两个int类型的变量做减法再将结果(标记为R2)压栈
这时候栈里应该就有R1和R2
指令7:从栈里弹出R1和R2做乘法并将结果压栈
指令8:进行int数据返回
到这里也就全部分析完了
网友评论