Щоб описати це, спочатку давайте зрозуміємо, як зберігаються локальні змінні та об'єкти.
Локальна змінна зберігається в стеці :
Якщо ви подивилися на зображення, ви повинні мати можливість зрозуміти, як все працює.
Коли виклик функції викликається програмою Java, кадр стека виділяється на стек викликів. Кадр стека містить параметри викликаного методу, його локальні параметри та зворотну адресу методу. Зворотна адреса позначає точку виконання, з якої виконання програми триває після повернення викликаного методу. Якщо для нового фрейма стека немає місця, то StackOverflowError
це викидається віртуальною машиною Java (JVM).
Найпоширеніший випадок, який, можливо, може вичерпати стек програми Java - це рекурсія. У рекурсії метод викликає себе під час його виконання. Рекурсія розглядається як потужна методика програмування загального призначення, але її слід використовувати обережно, щоб уникнути StackOverflowError
.
Приклад кидання a 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]