StackOverFlowError is one of the common confronted JVM
error. In this blog post lets learn inner mechanics of
thread stacks, reasons that can trigger StackOverFlowError and potential
solutions to address this error.
To gain deeper understanding in to StackOverFlowError,
let's review this simple program:
<<start:code>>
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");
}
}
<<end:code>>
This program is very simple with following execution
code:
1. main()
method is invoked first
2. main()
method invokes a() method. Inside a() method integer variable ‘x’ is
initialized to value 0.
3. a()
method in turn invokes b() method. Inside b() method Car object is constructed
and assigned to variable ‘y’.
4. b()
method in turn invokes c() method. Inside c() method 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. Thread adds the methods it’s
executing, primitive data types, object pointers, return values to its stack
frame in the sequence order in which they are executed.
Fig 1: Thread's
Stack frame.
In step #2: a() method is pushed in to 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 in to same stack
frame. Note both data i.e. ‘0’ and variable ‘x’ is pushed in to thread’s stack
frame.
In step #3: b() method is pushed in to thread’s stack. In
b() method, ‘Car’ object is created and assigned to variable ‘y’. Crucial point
to note here is ‘Car’ object is created in the heap and not in the thread’s stack. Only Car object’s reference
i.e. y is stored in the thread’s stack frame.
In step #4: c() method is pushed in to 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 in to same stack frame. Note
both data i.e. ‘0f’ and variable ‘z’ is pushed in to thread’s stack frame.
Once each method’s execution is completed, then method and
the variables/object pointers which are stored in the stack frame are removed
as show 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 StackOverflowError:
<<start:code>>
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();
}
}
<<end:code>>
In this program 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 few
thousand iterations thread’s stack size limit would be exceeded. Once stack
size limit is exceeded it will result in
'StackOverflowError':
<<start:code>>
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)
<<end:code>>
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
above example), threads stack size can grow to a large size. In those
circumstance, you must fix the source code which 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 ‘a()’ method.
2. Increase Thread Stack Size (-Xss)
There might be
legitimate reason where a threads stack size needs to be increased. May be
thread has to execute large number of methods or lot of local variables/created
in the methods thread has been executing. In such circumstance 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:
<<start:code>>
-Xss2m
<<end:code>>
This will set the
thread's stack size to 2 mb.
It might bring question, what
is the default thread’s stack size? Default thread stack size varies based on
your operating system, java version & 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
|
Every single day, millions & millions of people in
North America—bank, travel, and commerce—use the applications that RamLakshmanan has architected. Ram is an acclaimed speaker in major
conferences on scalability, availability, and performance topics. Recently, he
has founded a startup, which specializes in troubleshooting performance problems.