The StackOverflowError In Java

In this tutorial we’ll examine the StackOverflowError In Java, scenarios in which it occurs, and some approaches to resolving it.

In short, this error is thrown to indicate that the application call stack memory was exhausted. It can be the result of programming error, or recursive calls that run to deep.

Understanding The StackOverflowError

The StackOverflowError extends the VirtualMachineError class, this exception is normally an unrecoverable error within the JVM, such that, it has run out of resources.

When a method is invoked a new stack frame is created and allocated on the call stack. The stack frame contains the parameters, its local variables, and the method return address. The method return address indicates the point from which, the program execution should continue after the method returns.

This process continues until the root method call returns from all nested method calls. If these method calls exhaust the space available on the call stack, when an attempt is made to add a new stack frame for a method call, the StackOverflowError is thrown by the Java Virtual Machine.

A common case for exhausting an applications call stack is recursion. Which you probably already know is a method that invokes itself. Recursion is a powerful programming technique that can simplify the code that we write. Recursion has become more popular with the move toward functional programming.

However, modern languages have implemented tail recursion, that transforms recursive calls into non recursive calls, if the code is structured correctly, to take advantage of this feature. Unfortunately, Java does not support this feature out of the box.

StackOverflow Error Examples

The first example that we will look at is a StackOverflowError caused by programming error.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public static void programmingError() {
    int count = 1;
    while(count <=10) {
        try {
            programmingError();
        }
        catch(StackOverflowError sofe) {
            sofe.printStackTrace();
            throw sofe;
        }
    }
}

Above a StackOverflowError is raised due to not incrementing the count variable. Therefore, the loop never terminates and the programmingError method calls itself repeatedly until the call stack is exhausted. Moving on let’s say we are becoming a fan of functional programming and we decide to implement a method to add two numbers using recursion.

1
2
3
public static int addRecursivelyNoTerminationCondition(int x, int y)  {
    return addRecursivelyNoTerminationCondition(++x, --y);
}
1
2
3
4
@Test(expected = StackOverflowError.class)
public void addRecursively_noTerminationCondition() {
    StackOverflowErrorExample.addRecursivelyNoTerminationCondition(10,5);
}

Again, if we execute the above test the StackOverflowError will be raised, because we forgot the termination condition in the recursive call.
Let’s fix that now:

1
2
3
4
5
public static int addRecursively(int x, int y) {
    return y == 0
            ? x
            : addRecursively(++x, --y);
}

Above we add the termination condition y == 0 ? x and when we execute the below test everything works as expected.

1
2
3
4
@Test
public void addRecursively() {
    assertEquals(15, StackOverflowErrorExample.addRecursively(10,5));
}

However, what happens if we try to add with bigger numbers:

1
2
3
4
@Test(expected = StackOverflowError.class)
public void addRecursively_toDeep() {
    assertEquals(100000010, StackOverflowErrorExample.addRecursively(10, 100000000));
}

Executing the above code will result in StackOverflowError as to achieve the result the code results in many nested calls. These nested calls exceed the memory limit imposed on the call stack . Let’s look at one more example of triggering this type of error.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class ExampleClassOne {
    private ExampleClassTwo clsTwo = null;

    public ExampleClassOne() {
        clsTwo = new ExampleClassTwo();
    }
}

class ExampleClassTwo {
    private ExampleClassOne clsOne = null;

    public ExampleClassTwo() {
        clsOne = new ExampleClassOne();
    }
}

Above we have two classes: ExampleClassOne and ExampleClassTwo that create an instance of each other in their default constructors.

1
2
3
4
@Test(expected = StackOverflowError.class)
public void cyclicDependency() {
    StackOverflowErrorExample.cyclicDependency();
}

When we execute the above test a StackOverflowError is thrown due a cyclic dependency created by the default constructors of the example classes.

StackOverflowError Resolution

There are a couple of options to resolve this error.

Review And Fix Code

The first thing we should do is inspect the stack trace to identify where the error is. In the case of the programming error, or recursive calls we should see a repeating pattern.

1
2
3
4
5
java.lang.StackOverflowError
    at com.stephenenright.tutorials.corejavaexception.common.StackOverflowErrorExample.addRecursively(StackOverflowErrorExample.java:28)
    at com.stephenenright.tutorials.corejavaexception.common.StackOverflowErrorExample.addRecursively(StackOverflowErrorExample.java:28)
    at com.stephenenright.tutorials.corejavaexception.common.StackOverflowErrorExample.addRecursively(StackOverflowErrorExample.java:28)
    at com.stephenenright.tutorials.corejavaexception.common.StackOverflowErrorExample.addRecursively(StackOverflowErrorExample.java:28)

The stack trace above shows us that the issue is occurring at:

1
StackOverflowErrorExample.java:28

We can inspect the code to understand if the problem, is caused by a programming error or deeply nested method calls. For example, the error could manifest in the following scenarios:

  • recursive methods with no termination conditions
  • recursive methods that are too deep and exhaust the call stack memory limit.
  • Cyclic dependencies.
  • Instantiating a class within the same class as an instance variable of that class.

If the problem is caused by programming error, we can fix the code and resolve the issue. However, if the error is caused by recursive methods that run to deep, we can attempt to re-factor the code to a non recursive version.
For example the add recursive call could be re-factored to:

1
2
3
4
5
6
public static int addNotRecursive(int x, int y) {
    while (y-- > 0) {
        x = ++x;
    }
    return x;
}

Increase Stack Size Memory Limit

Another possibility that you may come across is to increase the stack memory limit.

1
-Xss3m

However, this will increase the stack size for all threads, thus wasting valuable resources. Normally, by default the stack size is from 320 KB to 1024KB. Moreover, this size can vary based on the environment: Java, JVM etc. For example, a 64 bit JVM running Java 8 has a limit of around 7,500 nested calls.

Conclusion

In this short tutorial we examined the StackOverflowError in Java. Some of its causes and approaches to resolving it.