Stackoverflow
we’ve all been there… :)
JVM will throw StackoverflowException when it detects that the thread stack memory space is full. This is similar situation we encounter when we run out of heap memory and get OutOfMemoryException. Most of the time, we’re experiencing stackoverflow exception when thread in the JVM application has too long call chain. That can result in eating up all stack memory by stack frames. JVM has safe defaults for stack memory region and usually it’s around 1MB. If you encounter StackoverflowException, most likely you’re using recursion in your application - either planned or not :)
What is stack space?
Everytime I refer to thread stack space in this article I refer to memory region described in JVM specification as Java Virtual Machine Stacks. This is region of memory that holds local variables and the list of method invocations.
Default stack size
The default thread stack space can be found by executing:
java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
Here is my output:
java -XX:+PrintFlagsFinal -version | grep ThreadStackSize
intx CompilerThreadStackSize = 1024 {pd product} {default}
intx ThreadStackSize = 1024 {pd product} {default}
intx VMThreadStackSize = 1024 {pd product} {default}
openjdk version "17.0.8" 2023-07-18 LTS
OpenJDK Runtime Environment Zulu17.44+15-CA (build 17.0.8+7-LTS)
OpenJDK 64-Bit Server VM Zulu17.44+15-CA (build 17.0.8+7-LTS, mixed mode, sharing)
How to deal with the stackoverflow?
Almost always you should focus on solving issues in your code by e.g.:
- limiting the recursion execution count
- rewriting the algorithm to an iterative version.
Note
There could be one more possible solution here - to use so called TCO - tail recursion optimization at the compiler level. Typically it’s transforming tail-recursive calls - the one where recursion is the only operation in the method - into loops before emitting compiled code (bytecode). Unfortunately as for 2024 java compiler does not support that optimization.
Use a hammer
There is a third option I’m describing for completeness. I can imagine a situation where we know how our recursion is working and it’s deepth is limited by known constant value. This call however is not possible to execute because of thread stack limitations. In this rare situation one can adjust the JVM stack thread memory size. To achieve this we setup JAVA_OPTS switch: e.g. to grow the stack twice:
java -jar -Xss2M sampleApp.jar
Note
It’s worth noting that maximum thread stack size supported on JDK 17 is 1024MB.
Note
If you’re curious how many invocations of the methods you can execute before filling the stack you can check this small code. Besides changing Xss option feel free to experiment with changing recursive method declarations and observe how that affects the results.
Other scenarios to use the Xss option?
Let’s say you need to run a large number of threads, and you can’t utilize project’s Loom Virtual threads feature ( :( ).
In this case each thread stack takes 1MB of RAM by default. For large number of threads like 10 000 this is already 10GB of RAM.
We could try to limit the size of thread’s stack space to optimize overall memory usage of the app.
The minimum threads stack in JVM I experienced on JDK 17 was 136KB. For 10 000 threads that makes a valid difference in memory usage (10 GB vs 1.36GB)!
comments powered by Disqus