Чтобы описать это, сначала давайте разберемся, как локальные переменные и объекты хранятся.
Локальные переменные хранятся в стеке :
Если вы посмотрите на изображение, вы сможете понять, как все работает.
Когда вызов функции вызывается приложением 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
, который задает размер стека потока равным 1 МБ, показан ниже:
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
Самое простое решение - тщательно проверить трассировку стека и
обнаружить повторяющуюся последовательность номеров строк. Эти номера строк
указать код, вызываемый рекурсивно. Как только вы обнаружите эти
линий, вы должны тщательно проверить свой код и понять, почему
рекурсия никогда не заканчивается.
Если вы убедились, что рекурсия
реализован правильно, вы можете увеличить размер стека, в
Чтобы разрешить большее количество вызовов. В зависимости от Java
Виртуальная машина (JVM) установлена, размер стека потока по умолчанию может
равно либо 512 КБ, либо 1 МБ . Вы можете увеличить стек потоков
размер с использованием флага -Xss
. Этот флаг может быть указан либо через
Конфигурация проекта или через командную строку. Формат
-Xss
Аргумент:
-Xss<size>[g|G|m|M|k|K]