lang. StackOverflowError indicates that the application stack is exhausted and is usually caused by deep or infinite recursion.
The most common cause of StackOverFlowError is excessively deep or infinite recursion. In Java: There are two areas in memory the heap and stack. The stack memory is used to store local variables and function call, while heap memory is used to store objects in Java.
The most common cause of stack overflow is recursion, which means a computer function called itself and is piling way too much stuff on the stack with no way to pop it back off.
It seems you're thinking that a stackoverflow error is like a buffer overflow exception in native programs, when there is a risk of writing into memory that had not been allocated for the buffer, and thus to corrupt some other memory locations. It's not the case at all.
JVM has a given memory allocated for each stack of each thread, and if an attempt to call a method happens to fill this memory, JVM throws an error. Just like it would do if you were trying to write at index N of an array of length N. No memory corruption can happen. The stack can not write into the heap.
A StackOverflowError is to the stack what an OutOfMemoryError is to the heap: it simply signals that there is no more memory available.
StackOverflowError: The Java Virtual Machine implementation has run out of stack space for a thread, typically because the thread is doing an unbounded number of recursive invocations as a result of a fault in the executing program.
Aren't there other ways for a stack overflow to occur, not only through recursion?
Sure. Just keep calling methods, without ever returning. You'll need a lot of methods, though, unless you allow recursion. Actually, it doesn't make a difference: a stack frame is a stack frame, whether it is one of a recursive method or not is the same.
The answer to your second question is: The stackoverflow is detected when the JVM tries to allocate the stack frame for the next call, and finds it is not possible. So, nothing will be overwritten.
Aren't there other ways for a stack overflow to occur, not only through recursion?
Challenge accepted :) StackOverflowError
without recursion (challenge failed, see comments):
public class Test
{
final static int CALLS = 710;
public static void main(String[] args)
{
final Functor[] functors = new Functor[CALLS];
for (int i = 0; i < CALLS; i++)
{
final int finalInt = i;
functors[i] = new Functor()
{
@Override
public void fun()
{
System.out.print(finalInt + " ");
if (finalInt != CALLS - 1)
{
functors[finalInt + 1].fun();
}
}
};
}
// Let's get ready to ruuuuuuumble!
functors[0].fun(); // Sorry, couldn't resist to not comment in such moment.
}
interface Functor
{
void fun();
}
}
Compile with standard javac Test.java
and run with java -Xss104k Test 2> out
. After that, more out
will tell you:
Exception in thread "main" java.lang.StackOverflowError
Now the idea is even simpler. Primitives in Java can be stored on the stack. So, let's declare a lot of doubles, like double a1,a2,a3...
. This script can write, compile and run the code for us:
#!/bin/sh
VARIABLES=4000
NAME=Test
FILE=$NAME.java
SOURCE="public class $NAME{public static void main(String[] args){double "
for i in $(seq 1 $VARIABLES);
do
SOURCE=$SOURCE"a$i,"
done
SOURCE=$SOURCE"b=0;System.out.println(b);}}"
echo $SOURCE > $FILE
javac $FILE
java -Xss104k $NAME
And... I got something unexpected:
#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007f4822f9d501, pid=4988, tid=139947823249152
#
# JRE version: 6.0_27-b27
# Java VM: OpenJDK 64-Bit Server VM (20.0-b12 mixed mode linux-amd64 compressed oops)
# Derivative: IcedTea6 1.12.6
# Distribution: Ubuntu 10.04.1 LTS, package 6b27-1.12.6-1ubuntu0.10.04.2
# Problematic frame:
# V [libjvm.so+0x4ce501] JavaThread::last_frame()+0xa1
#
# An error report file with more information is saved as:
# /home/adam/Desktop/test/hs_err_pid4988.log
#
# If you would like to submit a bug report, please include
# instructions how to reproduce the bug and visit:
# https://bugs.launchpad.net/ubuntu/+source/openjdk-6/
#
Aborted
It's 100% repetitive. This is related to your second question:
Does the StackOverflowError happen before the JVM actually overflows the stack or after?
So, in case of OpenJDK 20.0-b12 we can see that JVM firstly exploded. But it seems like a bug, maybe someone can confirm that in comments please, because I'm not sure. Should I report this? Maybe it's already fixed in some newer version... According to JVM specification link (given by JB Nizet in a comment) JVM should throw a StackOverflowError
, not die:
If the computation in a thread requires a larger Java Virtual Machine stack than is permitted, the Java Virtual Machine throws a StackOverflowError.
public class Test {
Test test = new Test();
public static void main(String[] args) {
new Test();
}
}
We want to create new Test
object. So, its (implicit) constructor will be called. But, just before that, all the members of Test
are initialized. So, Test test = new Test()
is executed first...
We want to create new Test
object...
Update: Bad luck, this is recursion, I asked question about that here.
There is no "StackOverFlowException". What you mean is "StackOverFlowError".
Yes you can continue working if you catch it because the stack is cleared when you do that but that would be a bad and ugly option.
When exactly the error is thrown ? - When you call a method and the JVM verifies if there is enough memory to do it. Of course, the error is thrown if it's not possible.
The most common cause of StackOverFlowError is excessively deep or infinite recursion.
For instance:
public int yourMethod(){
yourMethod();//infinite recursion
}
In Java:
There are two
areas in memory the heap and stack. The stack memory
is used to store local variables and function call, while heap memory
is used to store objects in Java.
If there is no memory left in stack for storing function call or local variable, JVM will throw java.lang.StackOverFlowError
while if there is no more heap space for creating object, JVM will throw java.lang.OutOfMemoryError
There are two main places that things can be stored in Java. The first is the Heap, that's used for dynamically allocated objects. new
.
In addition each running thread gets its own stack, and it gets an amount of memory allocated to that stack.
When you call a method then data is pushed into the stack to record the method call, the parameters being passed in, and any local variables being allocated. A method with five local variables and three parameters will use more stack space than a void doStuff()
method with no local variables will.
The main advantages of the stack are that there is no memory fragmentation, everything for one method call is allocated on the top of the stack, and that returning from methods is easy. To return from a method you just unwind the stack back to the previous method, set any value needed for the return value and you are done.
Because the stack is a fixed size per thread, (note that the Java Spec does not require a fixed size, but most JVM implementations at the time of writing use a fixed size) and because space on the stack is needed whenever you make a method call hopefully it should now be clear why it can run out and what can cause it to run out. There isn't a fixed number of method calls, there isn't anything specific about recursion, you get the exception which you try to call a method and there isn't enough memory.
Of course the size of stacks is set high enough that it is highly unlikely to happen in regular code. In recursive code though it can be quite easy to recurse to huge depths, and at that point you start running into this error.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With