本例以一个简单的加减乘除的例子,来熟悉虚拟机的操作数栈的工作流程。

 1package test;
 2
 3public class TestOperate {
 4    public int calc() {
 5        int a = 1;
 6        int b = 2;
 7        int c = 3;
 8        int d = 4;
 9        int r = (a - b + c) * d;
10        return r;
11    }
12
13}

该类对应的字节码指令如下:

 1Classfile /D:/develop/IdeaProjects/learning/test01/target/classes/test/TestOperate.class
 2  Last modified 2020-1-30; size 453 bytes
 3  MD5 checksum d2d815a9c2ff7ca0d14e3d492a46b05c
 4  Compiled from "TestOperate.java"
 5public class test.TestOperate
 6  minor version: 0
 7  major version: 51
 8  flags: ACC_PUBLIC, ACC_SUPER
 9Constant pool:
10   #1 = Methodref          #3.#21         // java/lang/Object."<init>":()V
11   #2 = Class              #22            // test/TestOperate
12   #3 = Class              #23            // java/lang/Object
13   #4 = Utf8               <init>
14   #5 = Utf8               ()V
15   #6 = Utf8               Code
16   #7 = Utf8               LineNumberTable
17   #8 = Utf8               LocalVariableTable
18   #9 = Utf8               this
19  #10 = Utf8               Ltest/TestOperate;
20  #11 = Utf8               calc
21  #12 = Utf8               ()I
22  #13 = Utf8               a
23  #14 = Utf8               I
24  #15 = Utf8               b
25  #16 = Utf8               c
26  #17 = Utf8               d
27  #18 = Utf8               r
28  #19 = Utf8               SourceFile
29  #20 = Utf8               TestOperate.java
30  #21 = NameAndType        #4:#5          // "<init>":()V
31  #22 = Utf8               test/TestOperate
32  #23 = Utf8               java/lang/Object
33{
34  public test.TestOperate();
35    descriptor: ()V
36    flags: ACC_PUBLIC
37    Code:
38      stack=1, locals=1, args_size=1
39         0: aload_0
40         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
41         4: return
42      LineNumberTable:
43        line 3: 0
44      LocalVariableTable:
45        Start  Length  Slot  Name   Signature
46            0       5     0  this   Ltest/TestOperate;
47
48  public int calc();
49    descriptor: ()I
50    flags: ACC_PUBLIC
51    Code:
52      stack=2, locals=6, args_size=1
53         0: iconst_1
54         1: istore_1
55         2: iconst_2
56         3: istore_2
57         4: iconst_3
58         5: istore_3
59         6: iconst_4
60         7: istore        4
61         9: iload_1
62        10: iload_2
63        11: isub
64        12: iload_3
65        13: iadd
66        14: iload         4
67        16: imul
68        17: istore        5
69        19: iload         5
70        21: ireturn
71      LineNumberTable:
72        line 5: 0
73        line 6: 2
74        line 7: 4
75        line 8: 6
76        line 9: 9
77        line 10: 19
78      LocalVariableTable:
79        Start  Length  Slot  Name   Signature
80            0      22     0  this   Ltest/TestOperate;
81            2      20     1     a   I
82            4      18     2     b   I
83            6      16     3     c   I
84            9      13     4     d   I
85           19       3     5     r   I
86}
87SourceFile: "TestOperate.java"

下面重点关注clac方法的方法体指令: image

image

 1   stack=2, locals=6, args_size=1
 2         0: iconst_1
 3         1: istore_1
 4         2: iconst_2
 5         3: istore_2
 6         4: iconst_3
 7         5: istore_3
 8         6: iconst_4
 9         7: istore        4
10         9: iload_1
11        10: iload_2
12        11: isub
13        12: iload_3
14        13: iadd
15        14: iload         4
16        16: imul
17        17: istore        5
18        19: iload         5
19        21: ireturn

栈深为2,局部变量为6: image

  1. iconst_1:把整型1压入栈顶
  2. istore_1:弹出栈顶元素,将栈顶元素的值设置到局部变量表中索引为1的变量中,也就是变量a的值现在为1
  3. iconst_2:将整型2压入栈顶
  4. istore_2:弹出栈顶元素,将其值2设置给局部变量表中的第2个变量b,也就是b=2
  5. iconst_3:将3压入栈顶
  6. istore_3:弹出栈顶元素3,将其设置到局部变量表的第3个变量c,也就是c=3
  7. iconst_4:将4压入栈顶
  8. istore 4:将栈顶元素弹出,将其值4设置到局部变量表的第4个元素d,也就是d=4
  9. iload_1:iload表示将局部变量中的值压入栈顶,这里是把第一个变量a的值1压入栈顶
  10. iload_2:将第二个变量的值2压入栈顶,此时已经入栈的值1进入栈底
  11. isub:弹出2个元素进行相减,将结果压入栈顶,这里依次弹出2和1,结果1-2=-1压入栈顶
  12. iload_3:将变量c的值3压入栈顶,此时已经入栈的元素-1进入栈底
  13. iadd:弹出2个元素相加,将结果压入栈顶,也就是此时栈顶元素的值是2
  14. iload 4:将第四个变量d的值4压入栈顶,这时栈顶为4,栈底为2
  15. imul:弹出2个元素,相乘后将结果压入栈顶,也就是此时栈顶为8,栈顶为空
  16. istore 5:将栈顶元素的值8弹出,设置到第5个局部变量r
  17. iload 5:将第5个局部变量r的值8压入栈顶
  18. ireturn:返回方法的结果,也就是弹出栈顶元素8,将其压入调用方法栈帧的操作数栈的栈顶,当前方法的操作数栈的其他元素将会全部被丢弃。