To describe this, first let us understand how local variables and objects are stored.
Local variable are stored on the stack:

If you looked at the image you should be able to understand how things are working.
When a function call is invoked by a Java application, a stack frame is allocated on the call stack. The stack frame contains the parameters of the invoked method, its local parameters, and the return address of the method. The return address denotes the execution point from which, the program execution shall continue after the invoked method returns. If there is no space for a new stack frame then, the StackOverflowError is thrown by the Java Virtual Machine (JVM).
The most common case that can possibly exhaust a Java application’s stack is recursion. In recursion, a method invokes itself during its execution. Recursion is considered as a powerful general-purpose programming technique, but it must be used with caution, to avoid StackOverflowError.
An example of throwing a StackOverflowError is shown below:
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);
}
}
In this example, we define a recursive method, called recursivePrint that prints an integer and then, calls itself, with the next successive integer as an argument. The recursion ends until we pass in 0 as a parameter. However, in our example, we passed in the parameter from 1 and its increasing followers, consequently, the recursion will never terminate.
A sample execution, using the -Xss1M flag that specifies the size of the thread stack to equal to 1 MB, is shown below:
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)
...
Depending on the JVM’s initial configuration, the results may differ, but eventually the StackOverflowError shall be thrown. This example is a very good example of how recursion can cause problems, if not implemented with caution.
How to deal with the StackOverflowError
-
The simplest solution is to carefully inspect the stack trace and
detect the repeating pattern of line numbers. These line numbers
indicate the code being recursively called. Once you detect these
lines, you must carefully inspect your code and understand why the
recursion never terminates. -
If you have verified that the recursion
is implemented correctly, you can increase the stack’s size, in
order to allow a larger number of invocations. Depending on the Java
Virtual Machine (JVM) installed, the default thread stack size may
equal to either 512 KB, or 1 MB. You can increase the thread stack
size using the-Xssflag. This flag can be specified either via the
project’s configuration, or via the command line. The format of the
-Xssargument is:
-Xss<size>[g|G|m|M|k|K]
В мире программистов ошибка «stack overflow» очень известна благодаря тому, что этот вид ошибки довольно распространен. А сам термин «stack overflow» известен еще больше, чем ошибка, благодаря одноименному англоязычному ресурсу «StackOverflow». Это сообщество программистов международного масштаба, и еще куча всего интересного. Поэтому не нужно путать ошибку «stack overflow» и веб-ресурс с таким же названием. В нашей статье речь пойдет об ошибке.
Ошибка «stack overflow» связана с переполнением стека. Она появляется в том случае, когда в стеке должно сохраниться больше информации, чем он может уместить. Объем памяти стека задает программист при запуске программы. Если в процессе выполнения программы стек переполняется, тогда возникает ошибка «stack overflow» и программа аварийно завершает работу. Причин возникновения подобной ошибки может быть несколько.
Ошибка «stack overflow»
Нужно отметить, что ошибка «stack overflow» не связана с конкретным языком программирования, то есть она может возникнуть в программах на Java, C++, C, C# и других компилируемых языках.
Причин ее возникновения может быть несколько. К самым распространенным причинам относятся:
бесконечная рекурсия;
глубокая рекурсия;
проблемы с переменными в стеке.
Бесконечная рекурсия и ошибка «stack overflow»
Бесконечная рекурсия редко возникает самостоятельно и по неизвестным причинам. Обычно программист:
забывает прописывать условие для выхода из рекурсии;
пишет неосознанную косвенную рекурсию.
Самая частая причина из категории «бесконечной рекурсии» — программист забывает прописывать условия выхода или же прописывает, но условия выхода не срабатывают.
Вот как это выглядит на С:
int factorial (int number)
{
if (number == 0)
return 1;
return number * factorial(number — 1);
}
В описанном примере прописаны условия выхода из рекурсии, однако они никогда не сработают, если «number» будет отрицательным. Поэтому через несколько миллионов вызовов стек будет переполнен и возникнет ошибка «stack overflow». В некоторых языках программирования предусмотрена «защита» от таких рекурсий. В них рекурсия из конца функции конвертируется в цикл, что не будет расходовать стековую память. Но подобная «оптимизация» вызовет другую, менее опасную проблему — «зацикливание».
Неосознанная бесконечная рекурсия возникает в том случае, если программист по невнимательности распределяет один и тот же функционал программы между разными нагруженными функциями, а потом делает так, что они друг друга вызывают.
В коде это выглядит так:
int Object::getNumber(int index, bool& isChangeable)
{
isChangeable = true;
return getNumber(index);
}
int Object::getNumber(int index)
{
bool noValue;
return getNumber(index, noValue);
}
Глубокая рекурсия и ошибка «stack overflow»
Глубокая рекурсия — это такая рекурсия, которая имеет свое окончание через определенное время, поэтому она не бесконечная. Однако памяти стека не хватит для завершения такой рекурсии, поэтому ошибка «stack overflow» обеспечена. Обычно такая ситуация заранее просчитывается программистом,поэтому ее можно решить. Например, можно:
отыскать другой программный алгоритм для решения поставленной задачи, чтобы избежать применения рекурсии;
«вынести» рекурсию за пределы аппаратного стека в динамический;
и другое.
Глубокая рекурсия выглядит так:
void eliminateList(struct Item* that)
{
if (that == NULL)
return;
eliminateList(that->next);
free(that);
}
Проблемы с переменными в стеке и ошибка «stack overflow»
Если взглянуть на популярность возникновения «stack overflow error», то причина с проблемными переменными в стеке стоит на первом месте. Кроется она в том, что программист изначально выделяет слишком много памяти локальной переменной.
Например:
int function() {
double b[1000000]
}
В данном случае может возникнуть такая ситуация, что массиву потребуется объем памяти, который стек не способен будет обеспечить, а значит, возникнет ошибка «stack overflow».
Заключение
Ошибка «stack overflow» возникает довольно часто. Каждый конкретный случай ее возникновения требует собственного решения. Одна причина объединяет возникновение такой ошибки — невнимательность программиста. Если «stack overflow error» возникла, значит, программист где-то что-то упустил или не доглядел.
To describe this, first let us understand how local variables and objects are stored.
Local variable are stored on the stack:

If you looked at the image you should be able to understand how things are working.
When a function call is invoked by a Java application, a stack frame is allocated on the call stack. The stack frame contains the parameters of the invoked method, its local parameters, and the return address of the method. The return address denotes the execution point from which, the program execution shall continue after the invoked method returns. If there is no space for a new stack frame then, the StackOverflowError is thrown by the Java Virtual Machine (JVM).
The most common case that can possibly exhaust a Java application’s stack is recursion. In recursion, a method invokes itself during its execution. Recursion is considered as a powerful general-purpose programming technique, but it must be used with caution, to avoid StackOverflowError.
An example of throwing a StackOverflowError is shown below:
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);
}
}
In this example, we define a recursive method, called recursivePrint that prints an integer and then, calls itself, with the next successive integer as an argument. The recursion ends until we pass in 0 as a parameter. However, in our example, we passed in the parameter from 1 and its increasing followers, consequently, the recursion will never terminate.
A sample execution, using the -Xss1M flag that specifies the size of the thread stack to equal to 1 MB, is shown below:
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)
...
Depending on the JVM’s initial configuration, the results may differ, but eventually the StackOverflowError shall be thrown. This example is a very good example of how recursion can cause problems, if not implemented with caution.
How to deal with the StackOverflowError
-
The simplest solution is to carefully inspect the stack trace and
detect the repeating pattern of line numbers. These line numbers
indicate the code being recursively called. Once you detect these
lines, you must carefully inspect your code and understand why the
recursion never terminates. -
If you have verified that the recursion
is implemented correctly, you can increase the stack’s size, in
order to allow a larger number of invocations. Depending on the Java
Virtual Machine (JVM) installed, the default thread stack size may
equal to either 512 KB, or 1 MB. You can increase the thread stack
size using the-Xssflag. This flag can be specified either via the
project’s configuration, or via the command line. The format of the
-Xssargument is:
-Xss<size>[g|G|m|M|k|K]
To describe this, first let us understand how local variables and objects are stored.
Local variable are stored on the stack:

If you looked at the image you should be able to understand how things are working.
When a function call is invoked by a Java application, a stack frame is allocated on the call stack. The stack frame contains the parameters of the invoked method, its local parameters, and the return address of the method. The return address denotes the execution point from which, the program execution shall continue after the invoked method returns. If there is no space for a new stack frame then, the StackOverflowError is thrown by the Java Virtual Machine (JVM).
The most common case that can possibly exhaust a Java application’s stack is recursion. In recursion, a method invokes itself during its execution. Recursion is considered as a powerful general-purpose programming technique, but it must be used with caution, to avoid StackOverflowError.
An example of throwing a StackOverflowError is shown below:
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);
}
}
In this example, we define a recursive method, called recursivePrint that prints an integer and then, calls itself, with the next successive integer as an argument. The recursion ends until we pass in 0 as a parameter. However, in our example, we passed in the parameter from 1 and its increasing followers, consequently, the recursion will never terminate.
A sample execution, using the -Xss1M flag that specifies the size of the thread stack to equal to 1 MB, is shown below:
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)
...
Depending on the JVM’s initial configuration, the results may differ, but eventually the StackOverflowError shall be thrown. This example is a very good example of how recursion can cause problems, if not implemented with caution.
How to deal with the StackOverflowError
-
The simplest solution is to carefully inspect the stack trace and
detect the repeating pattern of line numbers. These line numbers
indicate the code being recursively called. Once you detect these
lines, you must carefully inspect your code and understand why the
recursion never terminates. -
If you have verified that the recursion
is implemented correctly, you can increase the stack’s size, in
order to allow a larger number of invocations. Depending on the Java
Virtual Machine (JVM) installed, the default thread stack size may
equal to either 512 KB, or 1 MB. You can increase the thread stack
size using the-Xssflag. This flag can be specified either via the
project’s configuration, or via the command line. The format of the
-Xssargument is:
-Xss<size>[g|G|m|M|k|K]
StackOverflowError в Java
1. обзор
StackOverflowError может раздражать разработчиков Java, поскольку это одна из самых распространенных ошибок времени выполнения, с которыми мы можем столкнуться.
В этой статье мы увидим, как может возникать эта ошибка, на различных примерах кода и узнаем, как с ней бороться.
2. Фреймы стека и как происходитStackOverflowError
Давайте начнем с основ. When a method is called, a new stack frame gets created on the call stack. Этот кадр стека содержит параметры вызванного метода, его локальные переменные и адрес возврата метода, т.е. точка, с которой выполнение метода должно продолжаться после возврата вызванного метода.
Создание кадров стека будет продолжаться до тех пор, пока не будет достигнут конец вызовов методов, найденных во вложенных методах.
Во время этого процесса, если JVM обнаруживает ситуацию, когда нет места для создания нового кадра стека, она выдастStackOverflowError.
Наиболее частая причина, по которой JVM может столкнуться с этой ситуацией, —unterminated/infinite recursion — в описании Javadoc дляStackOverflowError упоминается, что ошибка возникает в результате слишком глубокой рекурсии в конкретном фрагменте кода.
Однако рекурсия не является единственной причиной этой ошибки. Это также может произойти в ситуации, когда приложение хранитcalling methods from within methods until the stack is exhausted. Это редкий случай, так как ни один разработчик не будет намеренно следовать плохой практике кодирования Другая редкая причина —having a vast number of local variables inside a method.
StackOverflowError также может быть выброшено, если приложение разработано для использованияcyclic relationships between classes. В этой ситуации конструкторы друг друга вызывают неоднократно, что приводит к возникновению этой ошибки. Это также можно рассматривать как форму рекурсии.
Другой интересный сценарий, который вызывает эту ошибку, — этоclass is being instantiated within the same class as an instance variable of that class. Это приведет к тому, что конструктор одного и того же класса будет вызываться снова и снова (рекурсивно), что в конечном итоге приведет кStackOverflowError.
В следующем разделе мы рассмотрим несколько примеров кода, демонстрирующих эти сценарии.
3. StackOverflowError в действии
В примере, показанном ниже,StackOverflowError будет выброшено из-за непреднамеренной рекурсии, когда разработчик забыл указать условие завершения для рекурсивного поведения:
public class UnintendedInfiniteRecursion {
public int calculateFactorial(int number) {
return number * calculateFactorial(number - 1);
}
}
Здесь ошибка выдается во всех случаях для любого значения, переданного в метод:
public class UnintendedInfiniteRecursionManualTest {
@Test(expected = StackOverflowError.class)
public void givenPositiveIntNoOne_whenCalFact_thenThrowsException() {
int numToCalcFactorial= 1;
UnintendedInfiniteRecursion uir
= new UnintendedInfiniteRecursion();
uir.calculateFactorial(numToCalcFactorial);
}
@Test(expected = StackOverflowError.class)
public void givenPositiveIntGtOne_whenCalcFact_thenThrowsException() {
int numToCalcFactorial= 2;
UnintendedInfiniteRecursion uir
= new UnintendedInfiniteRecursion();
uir.calculateFactorial(numToCalcFactorial);
}
@Test(expected = StackOverflowError.class)
public void givenNegativeInt_whenCalcFact_thenThrowsException() {
int numToCalcFactorial= -1;
UnintendedInfiniteRecursion uir
= new UnintendedInfiniteRecursion();
uir.calculateFactorial(numToCalcFactorial);
}
}
Однако в следующем примере указано условие завершения, но оно никогда не выполняется, если значение-1 передается методуcalculateFactorial(), что вызывает незавершенную / бесконечную рекурсию:
public class InfiniteRecursionWithTerminationCondition {
public int calculateFactorial(int number) {
return number == 1 ? 1 : number * calculateFactorial(number - 1);
}
}
Этот набор тестов демонстрирует этот сценарий:
public class InfiniteRecursionWithTerminationConditionManualTest {
@Test
public void givenPositiveIntNoOne_whenCalcFact_thenCorrectlyCalc() {
int numToCalcFactorial = 1;
InfiniteRecursionWithTerminationCondition irtc
= new InfiniteRecursionWithTerminationCondition();
assertEquals(1, irtc.calculateFactorial(numToCalcFactorial));
}
@Test
public void givenPositiveIntGtOne_whenCalcFact_thenCorrectlyCalc() {
int numToCalcFactorial = 5;
InfiniteRecursionWithTerminationCondition irtc
= new InfiniteRecursionWithTerminationCondition();
assertEquals(120, irtc.calculateFactorial(numToCalcFactorial));
}
@Test(expected = StackOverflowError.class)
public void givenNegativeInt_whenCalcFact_thenThrowsException() {
int numToCalcFactorial = -1;
InfiniteRecursionWithTerminationCondition irtc
= new InfiniteRecursionWithTerminationCondition();
irtc.calculateFactorial(numToCalcFactorial);
}
}
В этом конкретном случае ошибки можно было бы полностью избежать, если бы условие завершения было просто сформулировано как:
public class RecursionWithCorrectTerminationCondition {
public int calculateFactorial(int number) {
return number <= 1 ? 1 : number * calculateFactorial(number - 1);
}
}
Вот тест, который демонстрирует этот сценарий на практике:
public class RecursionWithCorrectTerminationConditionManualTest {
@Test
public void givenNegativeInt_whenCalcFact_thenCorrectlyCalc() {
int numToCalcFactorial = -1;
RecursionWithCorrectTerminationCondition rctc
= new RecursionWithCorrectTerminationCondition();
assertEquals(1, rctc.calculateFactorial(numToCalcFactorial));
}
}
Теперь давайте рассмотрим сценарий, в которомStackOverflowError возникает в результате циклических отношений между классами. Давайте рассмотримClassOne иClassTwo, которые создают экземпляры друг друга внутри своих конструкторов, вызывая циклическую связь:
public class ClassOne {
private int oneValue;
private ClassTwo clsTwoInstance = null;
public ClassOne() {
oneValue = 0;
clsTwoInstance = new ClassTwo();
}
public ClassOne(int oneValue, ClassTwo clsTwoInstance) {
this.oneValue = oneValue;
this.clsTwoInstance = clsTwoInstance;
}
}
public class ClassTwo {
private int twoValue;
private ClassOne clsOneInstance = null;
public ClassTwo() {
twoValue = 10;
clsOneInstance = new ClassOne();
}
public ClassTwo(int twoValue, ClassOne clsOneInstance) {
this.twoValue = twoValue;
this.clsOneInstance = clsOneInstance;
}
}
Теперь предположим, что мы пытаемся создать экземплярClassOne, как показано в этом тесте:
public class CyclicDependancyManualTest {
@Test(expected = StackOverflowError.class)
public void whenInstanciatingClassOne_thenThrowsException() {
ClassOne obj = new ClassOne();
}
}
В итоге получаетсяStackOverflowError, поскольку конструкторClassOne создает экземплярClassTwo,, а конструкторClassTwo снова создает экземплярClassOne.. И это происходит неоднократно, пока он не переполнится. стек.
Далее мы рассмотрим, что происходит, когда создается экземпляр класса в том же классе, что и переменная экземпляра этого класса.
Как видно в следующем примере,AccountHolder создает экземпляр переменной экземпляраjointAccountHolder:
public class AccountHolder {
private String firstName;
private String lastName;
AccountHolder jointAccountHolder = new AccountHolder();
}
Когда создается экземпляр классаAccountHolder,, вызываетсяStackOverflowError из-за рекурсивного вызова конструктора, как показано в этом тесте:
public class AccountHolderManualTest {
@Test(expected = StackOverflowError.class)
public void whenInstanciatingAccountHolder_thenThrowsException() {
AccountHolder holder = new AccountHolder();
}
}
4. Работа сStackOverflowError
Лучшее, что можно сделать при обнаруженииStackOverflowError, — это осторожно изучить трассировку стека, чтобы определить повторяющийся шаблон номеров строк. Это позволит нам найти код с проблемной рекурсией.
Давайте рассмотрим несколько трассировок стека, вызванных примерами кода, которые мы видели ранее.
Эта трассировка стека создаетсяInfiniteRecursionWithTerminationConditionManualTest, если мы опускаем объявление исключенияexpected:
java.lang.StackOverflowError
at c.b.s.InfiniteRecursionWithTerminationCondition
.calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5)
at c.b.s.InfiniteRecursionWithTerminationCondition
.calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5)
at c.b.s.InfiniteRecursionWithTerminationCondition
.calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5)
at c.b.s.InfiniteRecursionWithTerminationCondition
.calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5)
Здесь строка № 5 видна повторяющейся. Это где рекурсивный вызов делается. Теперь остается просто изучить код, чтобы убедиться, что рекурсия выполнена правильно.
Вот трассировка стека, которую мы получаем, выполняяCyclicDependancyManualTest (опять же, без исключенияexpected):
java.lang.StackOverflowError
at c.b.s.ClassTwo.(ClassTwo.java:9)
at c.b.s.ClassOne.(ClassOne.java:9)
at c.b.s.ClassTwo.(ClassTwo.java:9)
at c.b.s.ClassOne.(ClassOne.java:9)
Эта трассировка стека показывает номера строк, которые вызывают проблему в двух классах, которые находятся в циклическом отношении. Строка номер 9ClassTwo и строка номер 9ClassOne указывают на место внутри конструктора, где он пытается создать экземпляр другого класса.
Как только код тщательно проверен, и если ни одно из следующего (или любая другая логическая ошибка кода) не является причиной ошибки:
-
Неправильно реализованная рекурсия (т.е. без условия прекращения)
-
Циклическая зависимость между классами
-
Создание класса внутри того же класса, что и переменная экземпляра этого класса
Было бы неплохо попробовать увеличить размер стека. В зависимости от установленной JVM размер стека по умолчанию может варьироваться.
Флаг-Xss можно использовать для увеличения размера стека либо из конфигурации проекта, либо из командной строки.
5. Заключение
В этой статье мы более подробно рассмотрелиStackOverflowError, включая то, как код Java может вызывать это, и как мы можем диагностировать и исправить это.
Исходный код, относящийся к этой статье, можно найтиover on GitHub.
StackOverflowError просто сигнализирует о том, что памяти больше нет. Он расширяет класс VirtualMachineError, что указывает на то, что JVM (виртуальная машина Java) повреждена или у нее закончились ресурсы и она не может работать.
Если у вас есть такая функция, как:
int myFunction()
{
// ваш код
myFunction();
}
В приведенном выше коде myFunction() будет продолжать называть себя, все глубже и глубже, и когда пространство, используемое для отслеживания того, какие функции вы находитесь, заполнено, вы получаете ошибку stackoverflow. Общей причиной переполнения стека является плохой рекурсивный вызов. Как правило, это вызвано, когда ваши рекурсивные функции не имеют правильного условия завершения, поэтому он заканчивается тем, что навсегда называет себя.
Причина использования StackOverflowError
Параметры и локальные переменные выделяются в стеке. Стек обычно находится в верхнем конце вашего адресного пространства, и, поскольку он используется, он направляется к нижней части адресного пространства. У вашего процесса также есть куча, которая живет в нижней части вашего процесса. Когда вы выделяете память, эта куча может расти в верхнем конце вашего адресного пространства. Как вы можете видеть, существует вероятность того, что куча «столкнется» со стеклом. Если нет места для нового стека кадров, StackOverflowError вызывается виртуальной машиной Java (JVM).
Если стек заполнен, вы не можете нажать, если вы это сделаете, вы получите ошибку переполнения стека.
Если стек пуст, вы не можете поп, если вы это сделаете, вы получите ошибку стека стека.
Что такое stacktrace?
Столбец — очень полезный инструмент для отладки. Это список вызовов метода, в которых приложение было посередине, когда было выбрано исключение. Это очень полезно, потому что оно не только показывает вам, где произошла ошибка, но также и то, как программа оказалась в этом месте кода.
Столбец — очень полезный инструмент для отладки. Это список вызовов метода, в которых приложение было посередине, когда было выбрано исключение. Это очень полезно, потому что оно не только показывает вам, где произошла ошибка, но также и то, как программа оказалась в этом месте кода.
Источник: http://net-informations.com/Java/cJava/stackoverflow.htm
StackOverFlowError is one of the common confronted JVM errors. In this blog post, we will look at the inner mechanics of thread stacks, reasons that can trigger StackOverFlowError, and potential solutions to address this error.
To gain deeper understanding into StackOverFlowError, let’s review this simple program:
public class SimpleExample {
public static void main(String args[]) {
a()
}
public static void a() {
int x = 0;
b();
}
public static void b() {
Car y = new Car();
c();
}
public static void c() {
float z = 0f;
System.out.println("Hello");
}
}
This program is very simple with the following execution code:
-
main()method is invoked first -
main()method invokesa()method. Insidea()method, the integer variable ‘x’ is initialized to value 0. -
a()method in turn invokesb()method. Insideb()method, the Car object is constructed and assigned to variable ‘y.’ -
b()method in turn invokes thec()method. Inside thec()method, the float variable ‘z’ is initialized to value 0.
Now, let’s review what happens behind the scenes when above simple program is executed. Each thread in the application has its own stack. Each stack has multiple stack frames. The thread adds the methods it’s executing, primitive data types, object pointers, and return values to its stack frame in the sequence order in which they are executed.

Fig 1: Thread’s Stack frame
Step #1: main() method is pushed into the application thread’s stack.
Step #2: a() method is pushed into application thread’s stack. In a() method, primitive data type ‘int’ is defined with value 0 and assigned to variable x. This information is also pushed into the same stack frame. Note that both data, i.e. ‘0’ and variable ‘x,’ is pushed into thread’s stack frame.
Step #3: b() method is pushed into thread’s stack. In the b() method, the Car object is created and assigned to variable ‘y.’ A crucial point to note here is that the ‘Car’ object is created in the heap and not in the thread’s stack. Only the Car object’s reference, i.e. y, is stored in the thread’s stack frame.
Step #4: c() method is pushed into thread’s stack. In c() method, primitive data type ‘float’ is defined with value 0f and assigned to variable z. This information is also pushed into same stack frame. Note both data, i.e. ‘0f’ and variable ‘z,’ is pushed into thread’s stack frame.
Once each method’s execution is completed, then the method and the variables/object pointers are stored in the stack frame are removed, as shown in Fig 2.

Fig 2: Thread’s stack frame after executing methods
What Causes StackOverflowError?
As you can see, thread’s stack is storing methods it’s executing, primitive datatypes, variables, object pointers, and return values. All of these consume memory. If thread’s stack sizes grow beyond the allocated memory limit, then StackOverflowError is thrown. Let’s look at the below buggy program, which will result in a StackOverflowError:
public class SOFDemo {
public static void a() {
// Buggy line. It will cause method a() to be called infinite number of times.
a();
}
public static void main(String args[]) {
a();
}
}
In this program, the main() method invokes a() method. a() method recursively calls itself. This implementation will cause a() method to be invoked infinite number of times. In this circumstance, a() method will be added to thread’s stack frame infinite number of times. Thus, after a few thousand iterations, thread’s stack size limit would be exceeded. Once stack size limit is exceeded, it will result in StackOverflowError:
Exception in thread "main" java.lang.StackOverflowError
at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)
at com.buggyapp.stackoverflow.SOFDemo.a(SOFDemo.java:7)

Fig 3: StackOverflowError progression
What Are the Solutions to StackOverflowError?
There are couple of strategies to address StackOverflowError.
1. Fix the Code
Because of a non-terminating recursive call (as shown in the above example), threads stack size can grow to a large size. In those circumstances, you must fix the source code that is causing recursive looping. When ‘StackOverflowError’ is thrown, it will print the stacktrace of the code that it was recursively executing. This code is a good pointer to start debugging and fixing the issue. In the above example, it’s the a() method.
2. Increase Thread Stack Size (-Xss)
There might be legitimate reason where a threads stack size needs to be increased. Maybe thread has to execute large number of methods or lot of local variables/created in the methods thread has been executing? In such circumstances, you can increase the thread’s stack size using the JVM argument: ‘-Xss.» This argument needs to be passed when you start the application. Example:
-Xss2m
This will set the thread’s stack size to 2 mb.
It might bring a question: what is the default thread’s stack size? Default thread stack size varies based on your operating system, Java version, and vendor.
|
JVM version |
Thread stack size |
|
Sparc 32-bit JVM |
512k |
|
Sparc 64-bit JVM |
1024k |
|
x86 Solaris/Linux 32-bit JVM |
320K |
|
x86 Solaris/Linux 64-bit JVM |
1024K |
|
Windows 32-bit JVM |
320K |
|
Windows 64-bit JVM |
1024K |
Data Types
Frame (networking)
Java (programming language)
Java virtual machine
32-bit
64-bit
Primitive data type
Opinions expressed by DZone contributors are their own.
Improve Article
Save Article
Improve Article
Save Article
StackOverflowError is an error which Java doesn’t allow to catch, for instance, stack running out of space, as it’s one of the most common runtime errors one can encounter.
The main cause of the StackOverflowError is that we haven’t provided the proper terminating condition to our recursive function or template, which means it will turn into an infinite loop.
When does StackOverflowError encountered?
When we invoke a method, a new stack frame is created on the call stack or on the thread stack size. This stack frame holds parameters of the invoked method, mostly the local variables and the return address of the method. The creation of these stack frames will be iterative and will be stopped only when the end of the method invokes is found in the nested methods. In amidst of this process, if JVM runs out of space for the new stack frames which are required to be created, it will throw a StackOverflowError.
For example: Lack of proper or no termination condition. This is mostly the cause of this situation termed as unterminated or infinite recursion.
Given below is the implementation of infinite recursion:
public class StackOverflowErrorClass {
static int i = 0;
public static int printNumber(int x)
{
i = i + 2;
System.out.println(i);
return i + printNumber(i + 2);
}
public static void main(String[] args)
{
StackOverflowErrorClass.printNumber(i);
}
}
Runtime Error:
RunTime Error in java code :- Exception in thread “main” java.lang.StackOverflowError
at java.io.PrintStream.write(PrintStream.java:526)
at java.io.PrintStream.print(PrintStream.java:597)
at java.io.PrintStream.println(PrintStream.java:736)
at StackOverflowErrorClass.printNumber(StackOverflowErrorClass.java:13)
at StackOverflowErrorClass.printNumber(StackOverflowErrorClass.java:14)
at StackOverflowErrorClass.printNumber(StackOverflowErrorClass.java:14)
.
.
.
Note: Please run this on your system to see an error thrown, due to the stack size, this may not show error on online IDE.
How to fix StackOverflowError?
- Avoiding repetitive calls: Try to introduce a proper terminating condition or some condition for the recursive calls to ensure that it terminates.
Given below is the implementation with proper terminating condition:
publicclassstackOverflow {staticinti =0;publicstaticintprintNumber(intx){i = i +2;System.out.println(i);if(i ==10)returni;returni + printNumber(i +2);}publicstaticvoidmain(String[] args){stackOverflow.printNumber(i);}} - Increasing the Stack Size: The second method could be, if you notice that it’s implemented correctly still we see an error, then we can avoid that only by increasing the Stack Size in order to store the required number of recursive calls. This is achieved by changing the settings of the compiler.
Cyclic Relationships between classes is the relationship caused when two different classes instantiate each other inside their constructors.
StackOverflowError is encountered because the constructor of Class A1 is instantiating Class A2, and the constructor of Class A2 is again instantiating Class A1, and it occurs repeatedly until we see StackOverflow. This error is mainly due to the bad calling of constructors, that is, calling each other, which is not even required, and also it doesn’t hold any significance, so we can just avoid introducing them in the codes.
Given below is the implementation of Cyclic Relationships Between Classes:
publicclassA1 {publicA2 type2;publicA1(){type2 =newA2();}publicstaticvoidmain(String[] args){A1 type1 =newA1();}}classA2 {publicA1 type1;publicA2(){type1 =newA1();}}Runtime Error:
RunTime Error in java code :- Exception in thread “main” java.lang.StackOverflowError
at A2.(A1.java:32)
at A1.(A1.java:13)
at A2.(A1.java:32)
.
.
.Note: This will keep repeating, Infinite Recursion is seen by these infinite cyclic calls.
Troubleshooting
Problem
Stack Overflow exceptions can occur when a thread stack continues to grow in size until reaching the maximum limit.
Symptom
A stack overflow can result from:
- A deeply nested application
- An infinite loop within an application
- A problem in just-in-time (JIT) compiled code
- Applications requiring a larger stack size, especially ones relying on XML, GUI, or java2D classes.
- Native method calls
Stack overflow issues are frequently masked by Out of Memory exceptions. By resolving the memory constraints, the stack overflow can be resolved.
Cause
When a stack overflow occurs, the amount of stack space required by the program exceeds what is configured for the stack in the Java™ Virtual Machine (JVM) process, or the native stack size configured by the operating system.
Some applications require stacks that are larger than the default size; for example, a graphics-intensive Java program can require a larger stack, which may require an increase in the stack size to avoid StackOverflow.
Diagnosing The Problem
Look for either Out of Memory messages or java.lang.StackOverflow in the server logs. The process may continue to run after either of these messages are seen.
If a crash did occur, a javacore should have generated on IBM SDK. You will either see the signal as SIGSEGV, SIGILL, SIGABRT, or SIGBUS. Usually the current thread will indicate the following
Resolving The Problem
Explanations and Solutions
Detailed Configuration Options
Infinite Recursion
If an application is performing recursion, the maximum stack size can easily be reached and a Stack Overflow exception is thrown. The thread stack has a limited size and eventually its space will run out as the thread stack grows without bounds.
Some traits of recursion:
— Large thread stacks that appear to repeat
— An infinite loop that continuously spawns off threads
— Very large XML documents loaded into the Document Object Model (DOM)
— JSP or servlets calling itself (usually by executing forward or include to itself)
— Repeated calls in native functions
Increasing the thread stack size allows for larger thread stacks. However if the recursion continues to trigger a stack overflow, the next step is to identify what code is causing the recursion from javacores, thread dumps, or even system core files.
A thread stack that indicates it’s too large to display can be an indicator of stack overflow. This is especially true if the stack appears to repeat (such as recursive method calls).
JIT/HotSpot Compiled Code
The JIT/HotSpot compiler (JIT) is designed to speed up the JVM execution times by compiling method calls. This can speed up execution time, but as more aggressive optimizations are used, this can inadvertently cause recursion, resulting in stack overflow or crash. The documents linked below explain how to debug JIT and HotSpot compiler issues:
- JIT Problem Determination for IBM SDK 5.0 and 6.0 using -Xjit
- Debugging HotSpot failures on the Solaris and HP-UX operating environment
Depleted Native Stack
It is almost guaranteed that a crash will occur if the native stack runs out of space. System cores can be used to identify long and possibly recursively looping native thread stacks.
To resolve, increase the native stack size (usually by adjusting the operating system limit for stack size) to accommodate the recursive native calls; however, identifying the recursive native calls will help determine the root cause of the stack overflow.
![]()
Adjusting the Stack Sizes (Xss and Xmso) options
If this does not work, you can adjust the stack sizes. Doubling the stack size is suggested as a first step; however, every thread that the JVM creates will consume memory. Be careful to not exhaust your physical and heap memory resources.
For every Java thread, there are two stacks that are utilized. One is for Java code for method calls, and the other is for native C code (on Solaris and HP-UX, only the native stack is utilized). These are adjustable and can be raised to provide more room on the stacks to prevent an overflow.
- Maximum Thread Stack Size (-Xss)
This parameter controls the stack size of Java method calls (non-native) to track the state of variables. If you find looping code or large stacks that are all calling Java methods and do not make native calls, try raising the stack size by using the generic JVM argument below: Where <size> has the format, nn[k|m|g|K|M|G], such as -Xss512K
On Solaris and HP-UX systems which use the HotSpot JVM, there is no distinction between native and thread stack sizes. This is the only configurable value for all stack sizes; however, on HP-UX systems, there is another argument used to control stack size of the main method.
While the -Xss controls the stack size of all threads in native memory, -XX:MainThreadStackSize controls the size of the main thread. The main thread’s native size will be set to whichever value is higher.
Initial Thread Stack Size (-Xiss)
This is only for distributed platforms (AIX, Linux, Windows)
Adjusts the initial thread stack size that the JVM will start with. The default for all distributed platforms is 2KB. In most cases, you will not need to change this option.
Where <size> has the format, nn[k|m|g|K|M|G], such as -Xiss2K
Initial Native Stack Size (-Xmso)
This is only for distributed platforms (AIX, Linux, Windows)
This parameter controls the initial stack size of native (operating system) threads. Java code uses this to process calls made into native libraries, such as JIT or JNI calls. If there is an abundance of calls made on the native stack, adjust the native stack size using this generic JVM argument:
Where <size> has the format, nn[k|m|g|K|M|G], such as -Xmso512K.
NOTE: The maximum stack size for the operating system is controlled by ulimit -s on UNIX and Linux.
Default Values
This is a chart of default sizes for the various stack size arguments. This is subject to change due to upgrades of the SDK.
|
Value |
AIX |
Linux |
Windows |
Solaris |
HP-UX |
| Initial Thread Stack Size | 2KB | 2KB | 2KB | Not Adjustable | Not Adjustable |
| Maximum Thread Stack Size | 256KB (32-bit) 512KB (64-bit) |
256KB (32-bit) 512KB (64-bit) |
256KB (32-bit) 512KB (64-bit) |
Not Adjustable | Not Adjustable |
| Native Stack Size | 256KB | 256KB | 32KB (32-bit) 256KB (64-bit) |
512KB (32-bit) 1024KB (64-bit) |
64KB (PA-RISC) 1024KB (Itanium) |
Examples of Stack Overflow
Server Logs
These logs may contain references to StackOverflow, alongside other messages. Thread stacks may accompany these messages, indicating if there is a recursive call.
- SystemOut.log example
[3/14/15 3:14:15:926 CST] 31415926 BaseMap W CWOBJ0006W: An exception occurred:
com.ibm.websphere.objectgrid.ObjectGridRuntimeException: java.lang.StackOverflowError
<<Thread stack may follow this message>>
SystemErr.log example
[3/14/15 3:14:15:926 PST] 31415926 SystemErr R Caused by: java.lang.StackOverflowError
at java.util.Hashtable.get(Hashtable.java:461)
at com.ibm.ws.webcontainer.srt.SRTServletRequest.getParameter(SRTServletRequest.java:1257)
at psdi.webclient.system.session.WebClientSession.applySkin(WebClientSession.java:295)
at psdi.webclient.system.controller.AppInstance.render(AppInstance.java:1177)
at psdi.webclient.system.controller.AppInstance.render(AppInstance.java:1213)
at psdi.webclient.system.controller.AppInstance.render(AppInstance.java:1213)
…
Javacore
In javacores, look for «StackOverflowError» in the thread name entry:
System Dump (processed with jextract and DTFJ Dump Analyzer)
Look for this message near the end of a very large stack. This particular example had 1000 recursively called entries.
[{«Product»:{«code»:»SSEQTP»,»label»:»WebSphere Application Server»},»Business Unit»:{«code»:»BU053″,»label»:»Cloud & Data Platform»},»Component»:»Java SDK»,»Platform»:[{«code»:»PF002″,»label»:»AIX»},{«code»:»PF010″,»label»:»HP-UX»},{«code»:»PF016″,»label»:»Linux»},{«code»:»PF027″,»label»:»Solaris»},{«code»:»PF033″,»label»:»Windows»}],»Version»:»9.0;8.5.5;8.5;8.0;7.0″,»Edition»:»Base;Express;Liberty;Network Deployment»,»Line of Business»:{«code»:»LOB45″,»label»:»Automation»}},{«Product»:{«code»:»SSNVBF»,»label»:»Runtimes for Java Technology»},»Business Unit»:{«code»:»BU059″,»label»:»IBM Software w/o TPS»},»Component»:»Java SDK»,»Platform»:[{«code»:»»,»label»:»»}],»Version»:»»,»Edition»:»»,»Line of Business»:{«code»:»LOB36″,»label»:»IBM Automation»}}]