Introduction
StackOverflowError can be annoying for Java developers as it is one of the most common runtime errors we may encounter.
In this article, we will learn how this error occurs by looking at various code examples and how to handle it.
Stack Frames and StackOverflowerError Occur
Let’s start with the basics. When a method is called, a new stack frame is created on the call stack. This stack frame contains the parameters of the called method, its local variables and the return address of the method, i.e. the point at which execution of the method should continue after the called method returns.
The creation of the stack frame will continue until the end of the method call in the nested method is reached.
During this process, if the JVM encounters a situation where there is no room to create a new stack frame, it will throw a StackOverflower
error.
The most common reason the JVM encounters this is unterminated/infinite recursion - the Javadoc description of StackOverflowerr mentions that the error is triggered by the recursion being too deep in a particular code segment.
However, recursion is not the only cause of this error. It can also occur in cases where the application keeps calling methods from within methods until the stack is depleted. This is a rare case because no developer would intentionally follow poor coding practices. Another rare cause is when there are a large number of local variables in a method.
StackOverflowError can also be thrown when the application is designed to have a circular relationship between classes, in which case repeated calls to each other’s constructors are made, raising this error. This can also be considered as a form of recursion.
Another interesting scenario that causes this error is if a class is instantiated in the same class as an instance variable of that class. This will result in calling the constructor of the same class again and again (recursively), eventually leading to a stack overflow error.
StackOverflowerError is running
In the example shown below, a StackOverflowError error will be thrown due to accidental recursion where the developer forgot to specify a termination condition for the recursive behavior.
Here, for any value passed into the method, an error is raised in any case: the
|
|
However, in the next example, the termination condition is specified, but if the value -1
is passed to the calculateFactorial()
method, the termination condition is never met, which would result in unterminated/infinite recursion:.
This set of tests demonstrates this scenario.
|
|
In this particular case, if the termination condition is simply expressed as
The following test shows this in practice.
|
|
Now let’s look at a scenario where the StackOverflowError error occurs due to a circular relationship between classes. Let’s consider ClassOne
and ClassTwo
, which instantiate each other in their constructors, thus creating a cyclic relationship.
|
|
|
|
Now let’s suppose we try to instantiate ClassOne as shown in this test.
This eventually leads to a StackOverflowError error because the constructor of ClassOne
instantiates ClassTwo
, and the constructor of ClassTwo
instantiates ClassOne
again. This happens repeatedly until it overflows the stack.
Next, we will look at what happens when a class is instantiated in the same class as an instance variable of that class.
As the next example shows, AccountHolder
instantiates itself as the instance variable JointaCountHolder
.
When the AccountHolder
class is instantiated, a StackOverflowError error is raised due to a recursive call to the constructor, as shown in this test.
Resolving StackOverflowError
When encountering a StackOverflowError stack overflow error, the best practice is to carefully examine the stack trace to identify the repeating pattern of line numbers. This will allow us to locate the code with the problematic recurrence.
Let’s examine a few stack traces caused by the code examples we saw earlier.
If the expected exception declaration is ignored, this stack trace is generated by InfiniteCursionWithTerminationConditionManualTest
.
|
|
Here, you can see that line 5 is repeated. This is where the recursive call is made. Now it’s just a matter of checking the code to see if the recursion is completing in the right way.
Here is the stack trace we obtained by executing CyclicDependancyManualTest
(again, without the expected exceptions).
This stack trace shows the line numbers that are causing problems in both classes in the loop relationship. line 9 of ClassTwo and line 9 of ClassOne point to the location in the constructor where an attempt is made to instantiate another class.
After a thorough examination of the code, if any of the following (or any other code logic error) is not the cause of the error.
- incorrectly implemented recursion (i.e., no termination condition)
- circular dependencies between classes
- Instantiation of a class within the same class as an instance variable of that class
It is a good idea to try to increase the stack size. Depending on the installed JVM, the default stack size may vary.
The -Xss
flag can be used to increase the stack size from the project’s configuration or from the command line.
Conclusion
In this article, we took a closer look at the StackOverflower error, including how Java code can cause it, and how we can diagnose and fix it.