关于Java虚拟机(JVM)栈操作的理解

首先新建一个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数据返回

到这里也就全部分析完了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容