What actually causes a Stack Overflow error?
This question already has an answer here:
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.
Description from Virtual Machine Errors (§6.3)
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
Second try.
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.
Third try.
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.
链接地址: http://www.djcxy.com/p/14126.html上一篇: 用堆而不是堆栈在C中分配二维数组
下一篇: 什么实际上导致堆栈溢出错误?