[英]StackOverflowError during recursive call
这个问题严格来说是理论上的,但我找不到答案。 考虑这个简短的程序:
public class SBtst {
int i = 0;
public static void main(String[] args) {
new SBtst().rec();
}
public void rec() {
System.out.println("rec is called "+i++);
try {
rec();
} catch (StackOverflowError soe) {
System.out.println("completed "+soe.getStackTrace().length);
}
}
}
执行后我输出类似于:
rec is called 10472
rec is called 10473
rec is called 10474Recursion completed 1024
问题是:
为什么println()没有完成新行,因为根据我的理解,当执行下一个rec()调用时它应该抛出一个错误并且它是在AFTER println()完成之后
为什么我在stackTrace
数组中只获得1024个元素,但i
等于10474? 这是否意味着不是每个调用都在堆栈跟踪中?
为什么println()没有完成新行? 据我所知,执行下一个rec()调用时应抛出异常,但这将在println()完成后抛出。
你的理解不太正确。 在某些时候,堆栈看起来像:
…
rec()
rec()
rec()
println()
Println也消耗资源(请参阅mattingly890的答案 ),并且没有理由在那时你无法达到极限。
为什么我在堆栈跟踪中只获得1024个元素,即使至少使用了10474个? 这是否意味着不是每个调用都在堆栈跟踪中?
看看getStackTrace()的文档:
提供对printStackTrace()打印的堆栈跟踪信息的编程访问。 返回堆栈跟踪元素的数组,每个元素表示一个堆栈帧。 数组的第0个元素(假设数组的长度为非零)表示堆栈的顶部,这是序列中的最后一个方法调用。 通常,这是创建和抛出此throwable的点。 数组的最后一个元素(假设数组的长度为非零)表示堆栈的底部,这是序列中的第一个方法调用。
在某些情况下,某些虚拟机可能会从堆栈跟踪中省略一个或多个堆栈帧。 在极端情况下,允许没有关于此throwable的堆栈跟踪信息的虚拟机从此方法返回零长度数组。 一般来说,此方法返回的数组将包含由printStackTrace打印的每个帧的一个元素。 写入返回的数组不会影响将来对此方法的调用。
在评论中, Peter Lawrey找到了文档 ,其中提到了堆栈大小的1024默认限制:
Java HotSpot VM选项
-XX:ThreadStackSize = 512
线程堆栈大小(以KB为单位)。 (0表示使用默认堆栈大小)[Sparc:512; Solaris x86:320(在5.0及更早版本中为256之前的版本); Sparc 64位:1024; Linux amd64:1024(5.0及更早版本中为0); 所有其他的0.]
不过,可能还有其他因素可以发挥作用。 例如,这些问题提到了另外两个选项:
第1部分:
在OpenJDK实现中 , println()
实现如下所示:
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
因此,对print(x)
的调用可能会溢出堆栈,并且会在不newLine()
方法的情况下引发StackOverflowError
。
第2部分:
从Java API :
在某些情况下,某些虚拟机可能会从堆栈跟踪中省略一个或多个堆栈帧...一般来说,此方法返回的数组将包含每个将由printStackTrace打印的帧的元素。
因此,JVM可以自由地对堆栈跟踪中存储的堆栈帧数施加限制,例如1024。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.