在finally块中堆栈溢出错误处理
我有一个java程序,运行时间无限。
程序代码:
void asd()
{
try
{
//inside try block
System.out.println("Inside try !!!");
asd();
}
finally
{
//inside finally
System.out.println("Inside finally !!!");
asd();
}
}
OUTPUT:通过不断打印两个系统,该程序无限运行。
我的问题:在某些时候,它开始从try块中抛出StackOverflowErrors,所以它到达finally块,我们再次以递归方式调用该函数。 但是,在我们已经面对StackOverflowError的情况下,finally块中的递归函数是如何执行的呢?
JVM如何处理这种情况? 如果我们也得到OutOfMemoryErrors,是否会发生同样的行为?
问题在于你的示例程序是病态的。 它不会工作,并且它不能工作。
但是,finally块中的递归函数是如何执行的,因为我们已经面对StackOverflowError
。
有一个相当复杂的调用序列正在进行。 让我们假设堆栈可以容纳3帧。 “手动执行”为我们提供了一系列调用/调用堆栈快照,如下所示:
asd()
asd() > try
asd() > try > asd()
asd() > try > asd() > try
asd() > try > asd() > try > asd() // Stack Overflow!
asd() > try > asd() > finally
asd() > try > asd() > finally > asd() // Stack Overflow!
asd() > finally
asd() > finally > asd()
asd() > finally > asd() > try
asd() > finally > asd() > try > asd() // Stack Overflow!
asd() > finally > asd() > finally
asd() > finally > asd() > finally > asd() // Stack Overflow!
END
正如你可以看到深度为3的堆栈,我们做了7次调用,其中4次因堆栈溢出而失败。 如果您对深度为4的堆栈执行手动执行,则会得到15个调用,5 => 31.该模式是N => 2**N - 1 calls
。
在你的情况下,默认堆栈将能够容纳数百甚至数千次递归调用。
假设N = 100。2 ** 100是非常多的呼叫。 这不是无限的,但在程序终止之前你可能会死亡。
JVM如何处理这种情况?
如上。 JVM没有做任何特别的事情。 “有效的无限循环”行为完全取决于程序编写的方式。
如果我们也得到OutOfMemoryError
是否会发生同样的行为?
呃...这取决于你的程序。 但我相信你可以编写一个展示类似行为模式的示例程序。
假设程序正在执行asd()
方法并且堆栈空间即将结束。 另外,假设不是“内部尝试”和“最终内部”,而是打印一个计数器,告诉你堆栈的距离有多远:
void asd(int i){
try{
//inside try block
System.out.print(i);
System.out.println("t");
asd(i+1);
}
finally{
//inside finally
System.out.print(i);
System.out.println("f");
asd(i+1);
}
}
}
现在,这是程序在即将耗尽堆栈空间时所做的事情,当时i
是9154。
调用println("t")
输出字符,然后继续调用println()方法。 这会使程序用完堆栈空间,所以执行被移到finally
块。 打印新行时,再次调用println时,堆栈空间不足。 错误再次被抛出,并且执行在当前方法调用上方的激活帧中移动到finally
。 这使得程序第二次打印f
,并且由于我们已经从栈中弹出一个激活帧,所以这个调用现在可以正常完成,并打印出一个新行。 该计划迄今为止已经给出了这个结果:
...
9152t
9153t
9154t9154f9153f // notice we jumped to i=1953
现在,该方法再次调用自己,现在从finally块中调用。 堆栈空间的情况就像以前一样,所以程序像上面那样执行,除了因为我们在i = 1953的方法调用的finally块中,程序执行结束于方法调用的finally块我= 1952年:
9154t9154f9152f
i = 9152的finally块再次调用asd
,传递i = 9153,因为现在有足够的堆栈空间来打印从try-block输出的完整行:
9153t
然后继续自行调用,并在此调用中最终会再次耗尽堆栈空间,并提供输出:
9154t9154f9153f
...和其余的输出可以用类似的方式解释:
9154t9154f9151f
9152t
9153t
9154t9154f9153f
9154t9154f9152f
9153t
9154t9154f9153f
9154t9154f9150f
9151t
9152t
9153t
...
需要注意的是:
StackOverflowError
的情况下也会执行finally块。 StackOverflowError
程序可能处于不可预知的状态。 这里唯一可观察到的效果是println
不输出换行符。 在一个更复杂的程序中,这可能意味着你不能相信该程序正在处理的任何事情的状态,最安全的事情是完全纾解。 错误,即OutOfMemoryError,StackOverflowError等无意处理。 他们将JVM置于未定义状态,没有任何保证。 此时,您的应用程序必须简单地终止,并且您必须解决导致此问题的问题。
您的应用程序不应该尝试处理错误或在发生一次错误后运行。 如果你在这个时候调用递归函数,那么你就是应该指责的人。 结果是不可预测的。
请参阅“11.1.1。各种例外情况”:http://docs.oracle.com/javase/specs/jls/se7/html/jls-11.html
链接地址: http://www.djcxy.com/p/80457.html