作者:手机用户2502906263 | 来源:互联网 | 2022-11-20 11:30
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> Holger..:
由于堆栈的初始状态以及每条指令对它的影响是众所周知的,因此您可以随时精确地预测哪些项目将位于操作数堆栈上:
[ ] // 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处的指令之后达到一次.
然而,这并不意味着编译器必须执行这样的指令分析.编译器具有从源代码派生的代码的更高级别模型,称为抽象语法树.
该结构将用于生成结果字节码,并且它还可以预测该级别上所需的堆栈大小.但编译器实际上是如何做到的,取决于实现.