Java код:
public class SimpleRecursion {
public int factorial(int n) {
if (n == 0) {
return 1;
}
return n * factorial(n - 1);
}
}
Дает следующий байт-код для факторного метода (я выполнил 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 */
Я понимаю , что в пятой строке в блоке выше, стек = 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
верификатор виртуальной машины будет делать то, что предсказать содержимое стеки после каждой команды, чтобы проверить, подходит ли он в качестве входных данных последующего обучения. Но это помогает здесь, чтобы иметь заявленный максимальный размер, поэтому верификатор не нужно поддерживать динамически растущую структуру данных или предварительно выделить память для теоретически возможных записей в стеке 6ок. С заявленной максимального размера, он может остановиться, сталкиваясь инструкцию, которая будет толкать больше, чем это, так что никогда не нужно больше памяти, чем заявленный.
Как вы можете видеть, указанный максимальный размер стека достигается ровно один раз, сразу после iconst_1
обучения в индексе 9.
Это, однако, не означает , что компилятор должен будет выполнить такой анализ команд. Компилятор имеет более высокий уровень модели кода , полученного из исходного кода, известного как абстрактное синтаксическое дерево .
Эта структура будет использоваться для создания результирующих байт-кода, и он также может быть в состоянии предсказать требуемый размер стека на этом уровне уже. Но как компилятор на самом деле это делает, зависит от реализации.