Java字节码中的堆栈=4.Java编译器如何计算4的值?(堆栈的深度)



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个对象

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

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

[ ]            // 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堆栈条目预先分配内存。使用声明的最大大小,当遇到推送超过该大小的指令时,它可以停止,因此它永远不需要比声明的更多的内存。

正如您所看到的,声明的最大堆栈大小正好达到一次,就在索引9处的iconst_1指令之后。

然而,这并不意味着编译器必须执行这样的指令分析。编译器有一个从源代码派生的代码的更高级别模型,称为抽象语法树。

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

相关内容

  • 没有找到相关文章

最新更新