Java栈——操作数栈

先明确一个概念,Java栈(虚拟机栈)内存储的是栈帧,多个栈帧组成了一个虚拟机栈。栈帧是JVM进行方法调用使用的数据结构。

栈帧内保存包含有操作数栈。操作数栈保存了方法执行过程中各指令的操作数。

看下边代码:

public class Test1 {

    public int add(int a, int b) {
        int c = a + b;
        return c;
    }

    public static void main(String[] args) {
        Test1 test1 = new Test1();
        test1.add(1,1);
    }
}

对应的字节码文件:

Classfile <Path to /com/sanren1024/jvm/Test1.class>
  Last modified ...; size 578 bytes
  SHA-256 checksum fd3bf061ff99b334ba84db7afd21242589b527334dee24ff7d0410c0927aa9be
  Compiled from "Test1.java"
public class com.sanren1024.jvm.Test1
  minor version: 0
  major version: 57
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #7                          // com/sanren1024/jvm/Test1
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 3, attributes: 1
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Class              #8             // com/sanren1024/jvm/Test1
   #8 = Utf8               com/sanren1024/jvm/Test1
   #9 = Methodref          #7.#3          // com/sanren1024/jvm/Test1."<init>":()V
  #10 = Methodref          #7.#11         // com/sanren1024/jvm/Test1.add:(II)I
  #11 = NameAndType        #12:#13        // add:(II)I
  #12 = Utf8               add
  #13 = Utf8               (II)I
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               Lcom/sanren1024/jvm/Test1;
  #19 = Utf8               a
  #20 = Utf8               I
  #21 = Utf8               b
  #22 = Utf8               c
  #23 = Utf8               main
  #24 = Utf8               ([Ljava/lang/String;)V
  #25 = Utf8               args
  #26 = Utf8               [Ljava/lang/String;
  #27 = Utf8               test1
  #28 = Utf8               SourceFile
  #29 = Utf8               Test1.java
{
  public com.sanren1024.jvm.Test1();
    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 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/sanren1024/jvm/Test1;

  public int add(int, int);
    descriptor: (II)I
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=3
         0: iload_1
         1: iload_2
         2: iadd
         3: istore_3
         4: iload_3
         5: ireturn
      LineNumberTable:
        line 6: 0
        line 7: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   Lcom/sanren1024/jvm/Test1;
            0       6     1     a   I
            0       6     2     b   I
            4       2     3     c   I

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=2, args_size=1
         0: new           #7                  // class com/sanren1024/jvm/Test1
         3: dup
         4: invokespecial #9                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: iconst_1
        10: iconst_1
        11: invokevirtual #10                 // Method add:(II)I
        14: pop
        15: return
      LineNumberTable:
        line 11: 0
        line 12: 8
        line 13: 15
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      16     0  args   [Ljava/lang/String;
            8       8     1 test1   Lcom/sanren1024/jvm/Test1;
}
SourceFile: "Test1.java"

看到局部变量表上,slot1是test1实例。源码文件中调用了 test1.add(1,1) 方法,可以查看字节码的指令的执行。

  public int add(int, int);
    descriptor: (II)I
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=3
         0: iload_1             // 加载slot1操作数  slot1: a=1
         1: iload_2             // 加载slot2操作数  slot2: a=1
         2: iadd                // 执行add操作      结果:2
         3: istore_3            // 保存结果slot3    slot3:c=2
         4: iload_3             // 加载slot3
         5: ireturn             // 返回
      LineNumberTable:
        line 6: 0
        line 7: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       6     0  this   Lcom/sanren1024/jvm/Test1;
            0       6     1     a   I
            0       6     2     b   I
            4       2     3     c   I

对应的操作数栈内表现:

c(2)

2

1(b)

1(a)

最后返回,就返回栈顶的值。

在JVM的优化中,有时候,几个栈帧会共用一个操作数栈,来节约内存。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐