美文网首页
从JVM指令看i++

从JVM指令看i++

作者: 坠尘_ae94 | 来源:发表于2020-10-26 22:19 被阅读0次

    介绍

    最近在B站看面试题,碰到了这么一道题目1(参考 提供了链接),虽然感觉出题人是憨憨,题目出得没啥意思,转念一想,既然看到了并且花了好些时间才弄明白,那就顺便水一篇博客。

    题目如下:

    public class Main {
        public static void main(String[] args) {
            int i = 1;
            i = i++;
            int j = i++;
            int k = i + ++i * i++;
            System.out.println("i=" + i);
            System.out.println("j=" + j);
            System.out.println("k=" + k);
        }
    }
    

    给出程序运行结果。参考所提供的链接视频中给出了答题思路,但是我没听清楚,本文展示我的分析过程。

    首先,对生成的class文件进行反汇编,同时保留额外的编译信息:

    javap -v Main.class
    Classfile /D:/Private/Code/Java/Hello/target/classes/top/hellooooo/interview/Main.class
      Last modified 26 Oct 2020; size 1170 bytes
      MD5 checksum c05c1e0da276f3927cba38f6d89d57f7
      Compiled from "Main.java"
    public class top.hellooooo.interview.Main
      minor version: 0
      major version: 55
      flags: (0x0021) ACC_PUBLIC, ACC_SUPER
      this_class: #7                          // top/hellooooo/interview/Main
      super_class: #8                         // java/lang/Object
      interfaces: 0, fields: 0, methods: 2, attributes: 3
    Constant pool:
       #1 = Methodref          #8.#27         // java/lang/Object."<init>":()V
       #2 = Fieldref           #28.#29        // java/lang/System.out:Ljava/io/PrintStream;
       #3 = InvokeDynamic      #0:#33         // #0:makeConcatWithConstants:(I)Ljava/lang/String;
       #4 = Methodref          #34.#35        // java/io/PrintStream.println:(Ljava/lang/String;)V
       #5 = InvokeDynamic      #1:#33         // #1:makeConcatWithConstants:(I)Ljava/lang/String;
       #6 = InvokeDynamic      #2:#33         // #2:makeConcatWithConstants:(I)Ljava/lang/String;
       #7 = Class              #38            // top/hellooooo/interview/Main
       #8 = Class              #39            // java/lang/Object
       #9 = Utf8               <init>
      #10 = Utf8               ()V
      #11 = Utf8               Code
      #12 = Utf8               LineNumberTable
      #13 = Utf8               LocalVariableTable
      #14 = Utf8               this
      #15 = Utf8               Ltop/hellooooo/interview/Main;
      #16 = Utf8               main
      #17 = Utf8               ([Ljava/lang/String;)V
      #18 = Utf8               args
      #19 = Utf8               [Ljava/lang/String;
      #20 = Utf8               i
      #21 = Utf8               I
      #22 = Utf8               j
      #23 = Utf8               k
      #24 = Utf8               MethodParameters
      #25 = Utf8               SourceFile
      #26 = Utf8               Main.java
      #27 = NameAndType        #9:#10         // "<init>":()V
      #28 = Class              #40            // java/lang/System
      #29 = NameAndType        #41:#42        // out:Ljava/io/PrintStream;
      #30 = Utf8               BootstrapMethods
      #31 = MethodHandle       6:#43          // REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/in
    voke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite
    ;
      #32 = String             #44            // i=\u0001
      #33 = NameAndType        #45:#46        // makeConcatWithConstants:(I)Ljava/lang/String;
      #34 = Class              #47            // java/io/PrintStream
      #35 = NameAndType        #48:#49        // println:(Ljava/lang/String;)V
      #36 = String             #50            // j=\u0001
      #37 = String             #51            // k=\u0001
      #38 = Utf8               top/hellooooo/interview/Main
      #39 = Utf8               java/lang/Object
      #40 = Utf8               java/lang/System
      #41 = Utf8               out
      #42 = Utf8               Ljava/io/PrintStream;
      #43 = Methodref          #52.#53        // java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandle
    s$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
      #44 = Utf8               i=\u0001
      #45 = Utf8               makeConcatWithConstants
      #46 = Utf8               (I)Ljava/lang/String;
      #47 = Utf8               java/io/PrintStream
      #48 = Utf8               println
      #49 = Utf8               (Ljava/lang/String;)V
      #50 = Utf8               j=\u0001
      #51 = Utf8               k=\u0001
      #52 = Class              #54            // java/lang/invoke/StringConcatFactory
      #53 = NameAndType        #45:#58        // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang
    /invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
      #54 = Utf8               java/lang/invoke/StringConcatFactory
      #55 = Class              #60            // java/lang/invoke/MethodHandles$Lookup
      #56 = Utf8               Lookup
      #57 = Utf8               InnerClasses
      #58 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljav
    a/lang/Object;)Ljava/lang/invoke/CallSite;
      #59 = Class              #61            // java/lang/invoke/MethodHandles
      #60 = Utf8               java/lang/invoke/MethodHandles$Lookup
      #61 = Utf8               java/lang/invoke/MethodHandles
    {
      public top.hellooooo.interview.Main();
        descriptor: ()V
        flags: (0x0001) ACC_PUBLIC
        Code:
          stack=1, locals=1, args_size=1
             0: aload_0
             1: invokespecial #1                  // Method java/lang/Object."<init>":()V
             4: return
          LineNumberTable:
            line 8: 0
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0       5     0  this   Ltop/hellooooo/interview/Main;
    
      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: (0x0009) ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=4, args_size=1
             0: iconst_1
             1: istore_1
             2: iload_1
             3: iinc          1, 1
             6: istore_1
             7: iload_1
             8: iinc          1, 1
            11: istore_2
            12: iload_1
            13: iinc          1, 1
            16: iload_1
            17: iload_1
            18: iinc          1, 1
            21: imul
            22: iadd
            23: istore_3
            24: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            27: iload_1
            28: invokedynamic #3,  0              // InvokeDynamic #0:makeConcatWithConstants:(I)Ljava/lang/String;
            33: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            36: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            39: iload_2
            40: invokedynamic #5,  0              // InvokeDynamic #1:makeConcatWithConstants:(I)Ljava/lang/String;
            45: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            48: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            51: iload_3
            52: invokedynamic #6,  0              // InvokeDynamic #2:makeConcatWithConstants:(I)Ljava/lang/String;
            57: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            60: return
          LineNumberTable:
            line 10: 0
            line 11: 2
            line 12: 7
            line 13: 12
            line 14: 24
            line 15: 36
            line 16: 48
            line 17: 60
          LocalVariableTable:
            Start  Length  Slot  Name   Signature
                0      61     0  args   [Ljava/lang/String;
                2      59     1     i   I
               12      49     2     j   I
               24      37     3     k   I
        MethodParameters:
          Name                           Flags
          args
    }
    SourceFile: "Main.java"
    InnerClasses:
      public static final #56= #55 of #59;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
    BootstrapMethods:
      0: #31 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang
    /String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
        Method arguments:
          #32 i=\u0001
      1: #31 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang
    /String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
        Method arguments:
          #36 j=\u0001
      2: #31 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang
    /String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
        Method arguments:
          #37 k=\u0001
    
    

    取出main方法逻辑:

             0: iconst_1
             1: istore_1
             2: iload_1
             3: iinc          1, 1
             6: istore_1
             7: iload_1
             8: iinc          1, 1
            11: istore_2
            12: iload_1
            13: iinc          1, 1
            16: iload_1
            17: iload_1
            18: iinc          1, 1
            21: imul
            22: iadd
            23: istore_3
    

    JVM指令

    instruction description
    iconst_1 将int型1推送至栈顶
    istore_1 将栈顶int型数值存入第二个本地变量
    iload_1 将第二个int型本地变量推送至栈顶
    iinc 把一个常量值加到一个int类型的局部变量上
    istore_2 将栈顶int型数值存入第三个本地变量
    imul 将栈顶两int型数值相乘并将结果压入栈顶
    iadd 将栈顶两int型数值相加并将结果压入栈顶
    istore_3 将栈顶int型数值存入第四个本地变量

    解析

    本题主要涉及JVM Frame的Local Variables以及Operand Stacks(可看The Java® VirtualMachine Specification 2.6.1以及2.6.2)

    Operation LocalVariables(index:value) OperandStack(top->buttom)
    0: iconst_1 1
    1: istore_1 2:1(取出栈顶元素存入本地变量表) null
    2: iload_1 2:1 1
    3: iinc 1, 1 2:2 1
    6: istore_1 2:1 null
    7: iload_1 2:1 1
    8: iinc 1, 1 2:2 1
    11: istore_2 2:2 3:1 null
    12: iload_1 2:2 3:1 2
    13: iinc 1, 1 2:3 3:1
    16: iload_1 3 2
    17: iload_1 3 3 2
    18: iinc 1, 1 2:4 3:1
    21: imul 9 2
    22: iadd 11
    23: istore_3 2:4 3:1 4:11 null

    所以最后输出的结果为:4 1 11

    以上仅我一人看法,如发现错误烦请留言帮忙指出。

    小结

    这个小结是从视频结尾看到的,感觉非常不错,所以转载过来.视频就是参考1

    1. =右边的从左到右加载值一次压入操作数栈
    2. 自增,自减操作都是直接修改变量的值,不经过操作数栈
    3. 最后的赋值之前,临时结果也是存储在操作数栈中

    参考

    1. i++
    2. JVM 指令集简介
    3. The Java® VirtualMachine Specification

    相关文章

      网友评论

          本文标题:从JVM指令看i++

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