Чтобы описать это, сначала давайте понять, как хранятся локальные переменные и объекты.
Локальная переменная хранится в стеке: [/g1]
Если вы посмотрели на изображении вы должны понимать, как все работает.
Когда вызов функции вызывается Java-приложением, стек стека выделяется в стеке вызовов. Фрейм стека содержит параметры вызываемого метода, его локальные параметры и обратный адрес метода. Адрес возврата обозначает точку выполнения, из которой выполнение программы должно продолжаться после возврата вызванного метода. Если нет места для нового стека кадров, то StackOverflowError
вызывается виртуальной машиной Java (JVM).
Наиболее распространенным случаем, который может исчерпать стек Java-приложения, является рекурсия. В рекурсии метод запускается во время его выполнения. Рекурсия рассматривается как мощный метод программирования общего назначения, но ее следует использовать с осторожностью, чтобы избежать StackOverflowError
.
Пример, показывающий StackOverflowError
, показан ниже:
StackOverflowErrorExample.java:
public class StackOverflowErrorExample {
public static void recursivePrint(int num) {
System.out.println("Number: " + num);
if(num == 0)
return;
else
recursivePrint(++num);
}
public static void main(String[] args) {
StackOverflowErrorExample.recursivePrint(1);
}
}
В этом примере мы определяем рекурсивный метод, называемый recursivePrint
, который печатает целое число, а затем вызывает себя со следующим последовательным целым числом в качестве аргумента. Рекурсия заканчивается, пока мы не перейдем в 0
в качестве параметра. Однако в нашем примере мы передали параметр из 1 и его увеличивающих последователей, следовательно, рекурсия никогда не завершится.
Пример выполнения с использованием флага -Xss1M
, который определяет размер стека потоков ниже 1MB, показано ниже:
Number: 1
Number: 2
Number: 3
...
Number: 6262
Number: 6263
Number: 6264
Number: 6265
Number: 6266
Exception in thread "main" java.lang.StackOverflowError
at java.io.PrintStream.write(PrintStream.java:480)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221)
at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291)
at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104)
at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185)
at java.io.PrintStream.write(PrintStream.java:527)
at java.io.PrintStream.print(PrintStream.java:669)
at java.io.PrintStream.println(PrintStream.java:806)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:4)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
at StackOverflowErrorExample.recursivePrint(StackOverflowErrorExample.java:9)
...
В зависимости от начальной конфигурации JVM результаты могут отличаться, но в конечном итоге следует вызывать StackOverflowError
. Этот пример является очень хорошим примером того, как рекурсия может вызвать проблемы, если не выполнять их с осторожностью.
Как работать с StackOverflowError
-Xss
. Этот флаг можно указать либо через конфигурацию проекта, либо через командную строку. Формат аргумента -Xss
: -Xss<size>[g|G|m|M|k|K]
stringToArrayFn
может вызвать accessor.push(true)
и привести к тому, что ваш Array<Function | string>
будет содержать boolean
, что было бы плохо.
Используйте $ReadOnlyArray
вместо Array
в определении stringToArrayFn
, если он не намерен мутировать предоставленный массив.