Stack overflow ошибка как исправить

Это означает, что в стеке недостаточно места.

Причины — например, слишком глубокая рекурсия (редко), или слишком большие локальные переменные (куда чаще), или и то и другое сразу :)

Как избавиться? Опять же, можно просто в настройках компилятора поднять размер стека.

Но надежнее и лучше — посмотреть, нет ли слишком глубокой (вплоть до бесконечности) рекурсии, заменить локальные массивы на выделяемые динамически.

int f()
{
    int a[1000000];

практически гарантированно даст переполнение стека. В отличие от

int f()
{ 
    int * a = new int[1000000];  // Только не забудьте потом удалить...

или

    vector<int> a(1000000);

Словом, смотрите, кто съедает много стековой памяти, и избавляйтесь от него…

В мире программистов ошибка «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» возникла, значит, программист где-то что-то упустил или не доглядел.

Если вы столкнулись с ошибкой STATUS_STACK_OVERFLOW (0xC00000FD) в Windows, значит, произошло переполнение стека. Подобная ситуация может возникнуть в пользовательских потоках, если какая-либо функция задействует большое количество локальных переменных или использует рекурсивные запросы.

Что это такое?

Стеком называется область памяти, предназначенная для хранения локальных переменных и адресов возврата функций. Когда происходит вызов какой-либо функции, она резервирует место для своих переменных и записывает адрес, по которому будет возвращена по окончании работы. Закончив операцию, функция высвобождает пространство и передает управление по заданному адресу. Следовательно, стек функционирует по принципу LIFO (last in, first out) – последний пришел, первый ушел.

Переполнение стека – это сигнал, когда функция пытается выделить больше памяти, чем есть в свободном доступе. Причин этому может быть несколько:

  • Она задействует чрезмерно много локальных переменных, занимающих лишнее место.
  • Вызывает себя рекурсивно без условия выхода или с недостаточным его значением, что приводит к бесконечной циклической рекурсии и увеличению стека.
  • Не может расширить пространство, потому что файл подкачки переполнен или система не может выделить дополнительные страницы памяти.

Когда происходит подобная проблема, система генерирует сообщение STATUS_STACK_OVERFLOW и завершает поток или приложение с ошибкой.

Как исправить?

Для устранения неисправности STATUS_STACK_OVERFLOW необходимо устранить причину переполнения. В зависимости от того, где и как возникает ошибка, можно применить различные методы решения:

  • Если ошибка наблюдается в вашем приложении или коде, следует проверить логику функций и уменьшить количество локальных переменных либо глубину рекурсии. Также можно использовать другие структуры данных вместо стека, например, очередь или список.
  • Если ошибка встречается в чужом приложении или коде, следует связаться с разработчиком или поставщиком для получения обновления или исправления.

    Можно также попробовать запустить программу в режиме совместимости или с другими опциями.

  • Если причиной сбоя является недостаточный объем памяти или файла подкачки, необходимо освободить место на диске или увеличить размер этого элемента. Кроме того, попробуйте закрыть другие процессы, потребляющие много ресурсов.
  • Если ошибка возникает из-за повреждения системных файлов или реестра, необходимо просканировать и восстановить систему с помощью утилит sfc /scannow и dism /online /cleanup-image /restorehealth. Также можно попробовать сделать бэкап из точки восстановления.

С помощью этих рекомендаций вы сможете избавиться от ошибки STATUS_STACK_OVERFLOW и предотвратить ее появление в будущем.

The java.lang.StackOverflowError is a runtime error which points to serious problems that cannot be caught by an application. The java.lang.StackOverflowError indicates that the application stack is exhausted and is usually caused by deep or infinite recursion.

What Causes java.lang.StackOverflowError in Java

The java.lang.StackOverflowError occurs when the application stack continues to grow until it reaches the maximum limit. Some of the most common causes for a java.lang.StackOverflowError are:

  1. Deep or infinite recursion — If a method calls itself recursively without a terminating condition.
  2. Cyclic relationships between classes — If a class A instantiates an object of class B, which in turn instantiates an object of class A. This can be considered as a form of recursion.
  3. Memory intensive applications — Applications that rely on resource heavy objects such as XML documents, GUI or java2D classes.

java.lang.StackOverflowError Example in Java

Here is an example of java.lang.StackOverflowError thrown due to unintended recursion:

public class StackOverflowErrorExample {
    public void print(int myInt) {
        System.out.println(myInt);
        print(myInt);
    }

    public static void main(String[] args) {
        StackOverflowErrorExample soee = new StackOverflowErrorExample();
        soee.print(0);
    }
}

In this example, the recursive method print() calls itself over and over again until it reaches the maximum size of the Java thread stack since a terminating condition is not provided for the recursive calls. When the maximum size of the stack is reached, the program exits with a java.lang.StackOverflowError:

Exception in thread "main" java.lang.StackOverflowError
    at java.base/sun.nio.cs.UTF_8$Encoder.encodeLoop(UTF_8.java:564)
    at java.base/java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:585)
    at java.base/sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:301)
    at java.base/sun.nio.cs.StreamEncoder.implWrite(StreamEncoder.java:290)
    at java.base/sun.nio.cs.StreamEncoder.write(StreamEncoder.java:131)
    at java.base/java.io.OutputStreamWriter.write(OutputStreamWriter.java:208)
    at java.base/java.io.BufferedWriter.flushBuffer(BufferedWriter.java:120)
    at java.base/java.io.PrintStream.writeln(PrintStream.java:722)
    at java.base/java.io.PrintStream.println(PrintStream.java:938)
    at StackOverflowErrorExample.print(StackOverflowErrorExample.java:3)
    at StackOverflowErrorExample.print(StackOverflowErrorExample.java:4)
    at StackOverflowErrorExample.print(StackOverflowErrorExample.java:4)
    at StackOverflowErrorExample.print(StackOverflowErrorExample.java:4)
    at StackOverflowErrorExample.print(StackOverflowErrorExample.java:4)

How to fix java.lang.StackOverflowError in Java

Inspect the stack trace

Carefully inspecting the error stack trace and looking for the repeating pattern of line numbers enables locating the line of code with the recursive calls. When the line is identified, the code should be examined and fixed by specifying a proper terminating condition. As an example, the error stack trace seen earlier can be inspected:

at java.base/java.io.PrintStream.writeln(PrintStream.java:722)
at java.base/java.io.PrintStream.println(PrintStream.java:938)
at StackOverflowErrorExample.print(StackOverflowErrorExample.java:3)
at StackOverflowErrorExample.print(StackOverflowErrorExample.java:4)
at StackOverflowErrorExample.print(StackOverflowErrorExample.java:4)
at StackOverflowErrorExample.print(StackOverflowErrorExample.java:4)
at StackOverflowErrorExample.print(StackOverflowErrorExample.java:4)

In the above trace, line number 4 can be seen repeating, which is where the recursive calls are made and causing java.lang.StackOverflowError.

Increase Thread Stack Size (-Xss)

If the code has been updated to implement correct recursion and the program still throws a java.lang.StackOverflowError, the thread stack size can be increased to allow a larger number of invocations. Increasing the stack size can be useful, for example, when the program involves calling a large number of methods or using lots of local variables.

The stack size can be increased by changing the -Xss argument on the JVM, which can be set when starting the application. Here is an example:

-Xss4m

This will set the thread’s stack size to 4 mb which should prevent the JVM from throwing a java.lang.StackOverflowError.

Track, Analyze and Manage Errors With Rollbar

Rollbar in action

Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates Java error monitoring and triaging, making fixing errors easier than ever. Try it today.

1. Overview

StackOverflowError can be annoying for Java developers, as it’s one of the most common runtime errors we can encounter.

In this article, we’ll see how this error can occur by looking at a variety of code examples as well as how we can deal with it.

2. Stack Frames and How StackOverflowError Occurs

Let’s start with the basics. When a method is called, a new stack frame gets created on the call stack. This stack frame holds parameters of the invoked method, its local variables and the return address of the method i.e. the point from which the method execution should continue after the invoked method has returned.

The creation of stack frames will continue until it reaches the end of method invocations found inside nested methods.

During this process, if JVM encounters a situation where there is no space for a new stack frame to be created, it will throw a StackOverflowError.

The most common cause for the JVM to encounter this situation is unterminated/infinite recursion – the Javadoc description for StackOverflowError mentions that the error is thrown as a result of too deep recursion in a particular code snippet.

However, recursion is not the only cause for this error. It can also happen in a situation where an application keeps calling methods from within methods until the stack is exhausted. This is a rare case since no developer would intentionally follow bad coding practices. Another rare cause is having a vast number of local variables inside a method.

The StackOverflowError can also be thrown when an application is designed to have cyclic relationships between classes. In this situation, the constructors of each other are getting called repetitively which causes this error to be thrown. This can also be considered as a form of recursion.

Another interesting scenario that causes this error is if a class is being instantiated within the same class as an instance variable of that class. This will cause the constructor of the same class to be called again and again (recursively) which eventually results in a StackOverflowError.

In the next section, we’ll look at some code examples that demonstrate these scenarios.

3. StackOverflowError in Action

In the example shown below, a StackOverflowError will be thrown due to unintended recursion, where the developer has forgotten to specify a termination condition for the recursive behavior:

public class UnintendedInfiniteRecursion {
    public int calculateFactorial(int number) {
        return number * calculateFactorial(number - 1);
    }
}

Here, the error is thrown on all occasions for any value passed into the method:

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);
    }
}

However, in the next example a termination condition is specified but is never being met if a value of -1 is passed to the calculateFactorial() method, which causes unterminated/infinite recursion:

public class InfiniteRecursionWithTerminationCondition {
    public int calculateFactorial(int number) {
       return number == 1 ? 1 : number * calculateFactorial(number - 1);
    }
}

This set of tests demonstrates this scenario:

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);
    }
}

In this particular case, the error could have been completely avoided if the termination condition was simply put as:

public class RecursionWithCorrectTerminationCondition {
    public int calculateFactorial(int number) {
        return number <= 1 ? 1 : number * calculateFactorial(number - 1);
    }
}

Here’s the test that shows this scenario in practice:

public class RecursionWithCorrectTerminationConditionManualTest {
    @Test
    public void givenNegativeInt_whenCalcFact_thenCorrectlyCalc() {
        int numToCalcFactorial = -1;
        RecursionWithCorrectTerminationCondition rctc 
          = new RecursionWithCorrectTerminationCondition();

        assertEquals(1, rctc.calculateFactorial(numToCalcFactorial));
    }
}

Now let’s look at a scenario where the StackOverflowError happens as a result of cyclic relationships between classes. Let’s consider ClassOne and ClassTwo, which instantiate each other inside their constructors causing a cyclic relationship:

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;
    }
}

Now let’s say that we try to instantiate ClassOne as seen in this test:

public class CyclicDependancyManualTest {
    @Test(expected = StackOverflowError.class)
    public void whenInstanciatingClassOne_thenThrowsException() {
        ClassOne obj = new ClassOne();
    }
}

This ends up with a StackOverflowError since the constructor of ClassOne is instantiating ClassTwo, and the constructor of ClassTwo again is instantiating ClassOne. And this repeatedly happens until it overflows the stack.

Next, we will look at what happens when a class is being instantiated within the same class as an instance variable of that class.

As seen in the next example, AccountHolder instantiates itself as an instance variable jointAccountHolder:

public class AccountHolder {
    private String firstName;
    private String lastName;
    
    AccountHolder jointAccountHolder = new AccountHolder();
}

When the AccountHolder class is instantiated, a StackOverflowError is thrown due to the recursive calling of the constructor as seen in this test:

public class AccountHolderManualTest {
    @Test(expected = StackOverflowError.class)
    public void whenInstanciatingAccountHolder_thenThrowsException() {
        AccountHolder holder = new AccountHolder();
    }
}

4. Dealing With StackOverflowError

The best thing to do when a StackOverflowError is encountered is to inspect the stack trace cautiously to identify the repeating pattern of line numbers. This will enable us to locate the code that has problematic recursion.

Let’s examine a few stack traces caused by the code examples we saw earlier.

This stack trace is produced by InfiniteRecursionWithTerminationConditionManualTest if we omit the expected exception declaration:

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)

Here, line number 5 can be seen repeating. This is where the recursive call is being done. Now it’s just a matter of examining the code to see if the recursion is done in a correct manner.

Here is the stack trace we get by executing CyclicDependancyManualTest (again, without expected exception):

java.lang.StackOverflowError
  at c.b.s.ClassTwo.<init>(ClassTwo.java:9)
  at c.b.s.ClassOne.<init>(ClassOne.java:9)
  at c.b.s.ClassTwo.<init>(ClassTwo.java:9)
  at c.b.s.ClassOne.<init>(ClassOne.java:9)

This stack trace shows the line numbers that cause the problem in the two classes that are in a cyclic relationship. Line number 9 of ClassTwo and line number 9 of the ClassOne point to the location inside the constructor where it tries to instantiate the other class.

Once the code is being thoroughly inspected and if none of the following (or any other code logic error) is the cause of the error:

  • Incorrectly implemented recursion (i.e. with no termination condition)
  • Cyclic dependency between classes
  • Instantiating a class within the same class as an instance variable of that class

It would be a good idea to try and increase the stack size. Depending on the JVM installed, the default stack size could vary.

The -Xss flag can be used to increase the size of the stack, either from the project’s configuration or the command line.

5. Conclusion

In this article, we took a closer look at the StackOverflowError including how Java code can cause it and how we can diagnose and fix it.

Source code related to this article can be found over on GitHub.

Понравилась статья? Поделить с друзьями:
  • Ssd диск ошибка при загрузке
  • Ssd диск ошибка ввода вывода на устройстве
  • Ssd выдает ошибку при загрузке
  • Ssd m2 не форматируется ошибка
  • Ssd kingston ошибка ввода вывода