What is a StackOverflowError?

Viewed 785263

What is a StackOverflowError, what causes it, and how should I deal with them?

15 Answers

Parameters and local variables are allocated on the stack (with reference types, the object lives on the heap and a variable in the stack references that object on the heap). The stack typically lives at the upper end of your address space and as it is used up it heads towards the bottom of the address space (i.e. towards zero).

Your process also has a heap, which lives at the bottom end of your process. As you allocate memory, this heap can grow towards the upper end of your address space. As you can see, there is a potential for the heap to "collide" with the stack (a bit like tectonic plates!!!).

The common cause for a stack overflow is a bad recursive call. Typically, this is caused when your recursive functions doesn't have the correct termination condition, so it ends up calling itself forever. Or when the termination condition is fine, it can be caused by requiring too many recursive calls before fulfilling it.

However, with GUI programming, it's possible to generate indirect recursion. For example, your app may be handling paint messages, and, whilst processing them, it may call a function that causes the system to send another paint message. Here you've not explicitly called yourself, but the OS/VM has done it for you.

To deal with them, you'll need to examine your code. If you've got functions that call themselves then check that you've got a terminating condition. If you have, then check that when calling the function you have at least modified one of the arguments, otherwise there'll be no visible change for the recursively called function and the terminating condition is useless. Also mind that your stack space can run out of memory before reaching a valid terminating condition, thus make sure your method can handle input values requiring more recursive calls.

If you've got no obvious recursive functions then check to see if you're calling any library functions that indirectly will cause your function to be called (like the implicit case above).

If you have a function like:

int foo()
{
    // more stuff
    foo();
}

Then foo() will keep calling itself, getting deeper and deeper, and when the space used to keep track of what functions you're in is filled up, you get the stack overflow error.

Stack overflow means exactly that: a stack overflows. Usually there's a one stack in the program that contains local-scope variables and addresses where to return when execution of a routine ends. That stack tends to be a fixed memory range somewhere in the memory, therefore it's limited how much it can contain values.

If the stack is empty you can't pop, if you do you'll get stack underflow error.

If the stack is full you can't push, if you do you'll get stack overflow error.

So stack overflow appears where you allocate too much into the stack. For instance, in the mentioned recursion.

Some implementations optimize out some forms of recursions. Tail recursion in particular. Tail recursive routines are form of routines where the recursive call appears as a final thing what the routine does. Such routine call gets simply reduced into a jump.

Some implementations go so far as implement their own stacks for recursion, therefore they allow the recursion to continue until the system runs out of memory.

Easiest thing you could try would be to increase your stack size if you can. If you can't do that though, the second best thing would be to look whether there's something that clearly causes the stack overflow. Try it by printing something before and after the call into routine. This helps you to find out the failing routine.

A stack overflow is usually called by nesting function calls too deeply (especially easy when using recursion, i.e. a function that calls itself) or allocating a large amount of memory on the stack where using the heap would be more appropriate.

Like you say, you need to show some code. :-)

A stack overflow error usually happens when your function calls nest too deeply. See the Stack Overflow Code Golf thread for some examples of how this happens (though in the case of that question, the answers intentionally cause stack overflow).

The most common cause of stack overflows is excessively deep or infinite recursion. If this is your problem, this tutorial about Java Recursion could help understand the problem.

The stack has a space limit that depends on the operating system. The normal size is 8 MB (in Ubuntu (Linux), you can check that limit with $ ulimit -u and it can be checked in other OS similarly). Any program makes use of the stack at runtime, but to fully know when it is used you need to check the assembly language. In x86_64 for example, the stack is used to:

  1. Save the return address when making a procedure call
  2. Save local variables
  3. Save special registers to restore them later
  4. Pass arguments to a procedure call (more than 6)
  5. Other: random unused stack base, canary values, padding, ... etc.

If you don't know x86_64 (normal case) you only need to know when the specific high-level programming language you are using compile to those actions. For example in C:

  • (1) → a function call
  • (2) → local variables in function calls (including main)
  • (3) → local variables in function calls (not main)
  • (4) → a function call
  • (5) → normally a function call, it is generally irrelevant for a stack overflow.

So, in C, only local variables and function calls make use of the stack. The two (unique?) ways of making a stack overflow are:

  • Declaring too large local variables in main or in any function that it's called in (int array[10000][10000];)
  • A very deep or infinite recursion (too many function calls at the same time).

To avoid a StackOverflowError you can:

  • check if local variables are too big (order of 1 MB) → use the heap (malloc/calloc calls) or global variables.

  • check for infinite recursion → you know what to do... correct it!

  • check for normal too deep recursion → the easiest approach is to just change the implementation to be iterative.

Notice also that global variables, include libraries, etc... don't make use of the stack.

Only if the above does not work, change the stack size to the maximum on the specific OS. With Ubuntu for example: ulimit -s 32768 (32 MB). (This has never been the solution for any of my stack overflow errors, but I also don't have much experience.)

I have omitted special and/or not standard cases in C (such as usage of alloc() and similar) because if you are using them you should already know exactly what you are doing.

In a crunch, the below situation will bring a stack overflow error.

public class Example3 {

    public static void main(String[] args) {

        main(new String[1]);
    }

}

A simple Java example that causes java.lang.StackOverflowError because of a bad recursive call:

class Human {
    Human(){
        new Animal();
    }
}

class Animal extends Human {
    Animal(){
        super();
    }
}

public class Test01 {
    public static void main(String[] args) {
        new Animal();
    }
}
Related