我有一个java程序,运行无限次 .
程序代码:
void asd()
{
try
{
//inside try block
System.out.println("Inside try !!!");
asd();
}
finally
{
//inside finally
System.out.println("Inside finally !!!");
asd();
}
}
输出:通过不断打印两个系统,该程序无限运行 .
我的问题:在某些时候,它开始从try块中抛出StackOverflowErrors,因此它到达finally块,我们再次以递归方式调用此函数 . 但是,当我们已经面临StackOverflowError时,finally块中的递归函数如何执行?
JVM如何处理这种情况?如果我们也得到OutOfMemoryErrors会发生同样的行为吗?
5 回答
问题是你的示例程序是病态的 . 它不起作用,它无法工作 .
有一个相当复杂的调用序列正在进行 . 让我们假设堆栈可以容纳3帧 . “手动执行”为我们提供了一系列调用/调用堆栈快照,如下所示:
正如你所看到的深度为3的堆栈,我们进行了7次调用,其中4次失败,堆栈溢出 . 如果对深度为4的堆栈执行手动执行,则将获得15次调用,5 => 31.模式为
N => 2**N - 1 calls
.在您的情况下,默认堆栈将能够容纳数百甚至数千个递归调用 .
说N = 100. 2 ** 100是非常大量的呼叫 . 它不是无限的,但在程序终止之前你可能已经死了 .
如上 . JVM没有做任何特别的事情 . "effectively infinite loop"行为完全取决于程序的编写方式 .
呃......这取决于你的计划 . 但我相信你可以编写一个展示类似行为模式的示例程序 .
假设程序正在执行
asd()
方法,堆栈空间即将结束 . 还假设该方法不是"inside try"和"inside finally"而是打印一个计数器,告诉您堆栈的距离:}
现在,当
i
为9154时,这就是当程序即将耗尽堆栈空间时所执行的操作 .对
println("t")
的调用输出字符,然后继续调用println()方法 . 这使程序耗尽堆栈空间,因此执行将移至finally
块 . 打印新行时,再次调用println会耗尽堆栈空间 . 再次抛出错误,执行转到当前方法调用上方的激活框架中的finally
. 这使程序第二次打印f
,并且由于我们从堆栈中弹出一个激活帧,此调用现在正常完成,并打印出一个新行 . 到目前为止,该程序给出了这样的输出:现在,该方法再次调用自身,现在从finally块调用 . 堆栈空间的情况就像之前一样,所以程序执行如上,除了因为我们在i = 1953的方法调用的finally块中,程序执行结束于方法调用的finally块我= 1952年:
i = 9152的finally块再次调用
asd
,传递i = 9153,并且从现在开始有足够的堆栈空间来打印方法从try块输出的完整行:然后继续调用自己,在这个调用中将最终再次耗尽堆栈空间,给出输出:
......其余的输出可以用类似的方式解释:
重要的是要注意:
即使在
StackOverflowError
的情况下也会执行finally块 .遇到
StackOverflowError
的程序可能处于不可预测的状态 . 这里唯一可观察到的影响是println
不信任该程序正在进行的任何事情的状态,并且最安全的做法是完全拯救 .不打算处理错误,即OutOfMemoryError,StackOverflowError等 . 它们使JVM处于未定义状态,没有任何保证 . 此时,您的应用程序必须简单地终止,您必须解决导致此问题的问题 .
您的应用程序不应尝试处理错误或在发生错误后运行 . 如果您此时正在调用递归函数,那么您应该受到责备 . 结果是不可预测的 .
见"11.1.1. The Kinds of Exceptions":http://docs.oracle.com/javase/specs/jls/se7/html/jls-11.html
你会得到同样的在finally块中调用
asd
时出错 - 您需要让堆栈上的asd
帧返回/弹出以解决错误如果您尝试处理
StackOverflowError
,它将只会导致更多的后续StackOverflowErrors,因为您正在尝试在已经完整的堆栈上执行进一步的执行 . 您不应该尝试捕获,使用此信息来更好地构建代码 . 这是java中未经检查的异常的要点 . 如果您不熟悉不同类型的例外......已检查的例外情况
检查异常是在编译时检查异常并表示需要处理的条件(通过try-catch语句),因为它超出了程序的控制范围 . 例如,如果要在给定线程内的任何位置调用
Thread.sleep()
,则需要处理在线程被另一个线程中断时可能发生的潜在InterruptedException
. 由于这种情况的可能性在编译时是已知的,因此您需要程序员来处理这种情况 .未经检查的例外情况
java.lang.Throwable班说......
由于
StackOverflowError
是Error
的子类,因此不会将其视为已检查的异常,因此"unchecked" . Unchecked exceptions often arise due to programming errors, e.g. in your case, the incorrect termination of an infinite recursion . 如果您尝试访问数组中的空变量或无效索引的某些成员,则可能会出现其他未经检查的异常 . 这些异常几乎不可能在编译时检查,并且更常用于向程序员显示他或她的代码中的错误 .Further Reading
Java Exceptions
Checked vs Unchecked Exceptions