在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

    上一篇: Stack overflow error handling in finally block

    下一篇: Why does this method print 4?