美文网首页
关于Java虚拟机(JVM)栈操作的理解

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

作者: zhd______ | 来源:发表于2020-07-22 11:59 被阅读0次

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

    到这里也就全部分析完了

    相关文章

      网友评论

          本文标题:关于Java虚拟机(JVM)栈操作的理解

          本文链接:https://www.haomeiwen.com/subject/iryclktx.html