代码之家  ›  专栏  ›  技术社区  ›  Youness

堆栈=4在Java字节码中。Java编译器如何计算4值?(烟囱深度)

  •  2
  • Youness  · 技术社区  · 6 年前

    Java代码:

    public class SimpleRecursion {
    
        public int factorial(int n) {
            if (n == 0) {
                return 1;
            }
            return n*factorial(n - 1);
        }
    
    }
    

    为factorial方法提供以下字节码(我执行了javap来生成它):

    public int factorial(int); 
    descriptor: (I)I 
    flags: ACC_PUBLIC 
    Code:   
      stack=4, locals=2, args_size=2
         0: iload_1
         1: ifne          6
         4: iconst_1
         5: ireturn
         6: iload_1
         7: aload_0
         8: iload_1
         9: iconst_1
        10: isub
        11: invokevirtual #2                  // Method factorial:(I)I
        14: imul
        15: ireturn   
      LineNumberTable:
        line 4: 0
        line 5: 4
        line 7: 6   
      StackMapTable: number_of_entries = 1
        frame_type = 6 /* same */
    

    我知道在上面街区的第五行, Stack=4表示堆栈最多可以有4个对象 .

    但是编译器是如何计算的呢?

    1 回复  |  直到 6 年前
        1
  •  6
  •   Holger    6 年前

    由于堆栈的初始状态以及每个指令对其的影响是众所周知的,因此您可以精确地预测在任何时候操作数堆栈上会出现哪种项:

    [ ]            // initially empty
    [ I ]          0: iload_1
    [ ]            1: ifne          6
    [ I ]          4: iconst_1
    [ ]            5: ireturn
    [ I ]          6: iload_1
    [ I O ]        7: aload_0
    [ I O I ]      8: iload_1
    [ I O I I ]    9: iconst_1
    [ I O I ]     10: isub
    [ I I ]       11: invokevirtual #2   // Method factorial:(I)I
    [ I ]         14: imul
    [ ]           15: ireturn   
    

    jvm_的验证器将精确地做到这一点,预测每个指令后堆栈的内容,以检查它是否适合作为后续指令的输入。但是在这里有一个声明的最大大小是有帮助的,因此验证器不需要为理论上可能的64K堆栈条目维护动态增长的数据结构或预分配内存。使用声明的最大大小,它可以在遇到推多的指令时停止,因此它从不需要比声明的内存更多的内存。

    如您所见,声明的最大堆栈大小在 iconst_1 索引9的说明。

    然而,这并不意味着编译器必须执行这样的指令分析。编译器具有从源代码派生的更高级别的代码模型,称为 Abstract syntax tree .

    这个结构将被用来生成所得到的字节码,并且它可能已经能够预测该级别上所需的堆栈大小。但是编译器实际上是如何做到的,这取决于实现。

    推荐文章