Try-finally块阻止StackOverflowError

本文翻译自:Try-finally block prevents StackOverflowError

Take a look at the following two methods: 看看以下两种方法:

public static void foo() {
    try {
        foo();
    } finally {
        foo();
    }
}

public static void bar() {
    bar();
}

Running bar() clearly results in a StackOverflowError , but running foo() does not (the program just seems to run indefinitely). 运行bar()显然会导致StackOverflowError ,但运行foo()不会(程序似乎无限期运行)。 Why is that? 这是为什么?


#1楼

参考:https://stackoom.com/question/qBta/Try-finally块阻止StackOverflowError


#2楼

When you get an exception from the invocation of foo() inside the try , you call foo() from finally and start recursing again. 当你从调用异常foo()里面try ,你叫foo()finally并再次开始递归。 When that causes another exception, you'll call foo() from another inner finally() , and so on almost ad infinitum . 当它导致另一个异常时,你将从另一个内部finally()调用foo() finally() ,几乎无限制地调用它。


#3楼

Try running the following code: 尝试运行以下代码:

    try {
        throw new Exception("TEST!");
    } finally {
        System.out.println("Finally");
    }

You will find that the finally block executes before throwing an Exception up to the level above it. 你会发现finally块在抛出异常直到它上面的级别之前执行。 (Output: (输出:

Finally 最后

Exception in thread "main" java.lang.Exception: TEST! 线程“main”中的异常java.lang.Exception:TEST! at test.main(test.java:6) 在test.main(test.java:6)

This makes sense, as finally is called right before exiting the method. 这是有道理的,因为最终在退出方法之前被调用。 This means, however, that once you get that first StackOverflowError , it will try to throw it, but the finally must execute first, so it runs foo() again, which gets another stack overflow, and as such runs finally again. 但是,这意味着,一旦你得到第一个StackOverflowError ,它将尝试抛出它,但是finally必须先执行,因此它再次运行foo() ,这会再次发生堆栈溢出,并且最终再次运行。 This keeps happening forever, so the exception is never actually printed. 这种情况永远发生,因此异常永远不会被打印出来。

In your bar method however, as soon as the exception occurs, it is just thrown straight up to the level above, and will be printed 但是,在bar方法中,一旦发生异常,它就会直接抛到上面的级别,然后打印出来


#4楼

Learn to trace your program: 学会追踪你的计划:

public static void foo(int x) {
    System.out.println("foo " + x);
    try {
        foo(x+1);
    } 
    finally {
        System.out.println("Finally " + x);
        foo(x+1);
    }
}

This is the output I see: 这是我看到的输出:

[...]
foo 3439
foo 3440
foo 3441
foo 3442
foo 3443
foo 3444
Finally 3443
foo 3444
Finally 3442
foo 3443
foo 3444
Finally 3443
foo 3444
Finally 3441
foo 3442
foo 3443
foo 3444
[...]

As you can see the StackOverFlow is thrown at some layers above, so you can do additional recursion steps till you hit another exception, and so on. 正如您所看到的,StackOverFlow在上面的某些层上抛出,因此您可以执行其他递归步骤,直到遇到另一个异常,依此类推。 This is an infinite "loop". 这是一个无限的“循环”。


#5楼

It doesn't run forever. 它不会永远运行。 Each stack overflow causes the code to move to the finally block. 每个堆栈溢出都会导致代码移动到finally块。 The problem is that it will take a really, really long time. 问题是它需要非常长的时间。 The order of time is O(2^N) where N is the maximum stack depth. 时间顺序为O(2 ^ N),其中N是最大堆栈深度。

Imagine the maximum depth is 5 想象一下,最大深度为5

foo() calls
    foo() calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
    finally calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
finally calls
    foo() calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
    finally calls
       foo() calls
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()
       finally
           foo() calls
              foo() which fails to call foo()
           finally calls
              foo() which fails to call foo()

To work each level into the finally block take twice as long an the stack depth could be 10,000 or more. 要将每个级别工作到finally块中需要两倍的时间,堆栈深度可以是10,000或更多。 If you can make 10,000,000 calls per second, this will take 10^3003 seconds or longer than the age of the universe. 如果您每秒可以拨打10,000,000个电话,则需要10 ^ 3003秒或更长的时间。


#6楼

In effort to provide reasonable evidence that this WILL eventually terminate, I offer the following rather meaningless code. 为了提供合理的证据表明这将最终终止,我提供以下相当无意义的代码。 Note: Java is NOT my language, by any stretch of the most vivid imagination. 注意:Java不是我的语言,在任何一个最生动的想象中。 I proffer this up only to support Peter's answer, which is the correct answer to the question. 我提出这个问题只是为了支持彼得的答案,这是对这个问题正确答案。

This attempts to simulate the conditions of what happens when an invoke can NOT happen because it would introduce a stack overflow. 这会尝试模拟调用无法发生时所发生的情况,因为它会引入堆栈溢出。 It seems to me the hardest thing people are failing to grasp in that the invoke does not happen when it cannot happen. 在我看来,最难的事情人们都未能把握,当不能发生它的调用不会发生。

public class Main
{
    public static void main(String[] args)
    {
        try
        {   // invoke foo() with a simulated call depth
            Main.foo(1,5);
        }
        catch(Exception ex)
        {
            System.out.println(ex.toString());
        }
    }

    public static void foo(int n, int limit) throws Exception
    {
        try
        {   // simulate a depth limited call stack
            System.out.println(n + " - Try");
            if (n < limit)
                foo(n+1,limit);
            else
                throw new Exception("StackOverflow@try("+n+")");
        }
        finally
        {
            System.out.println(n + " - Finally");
            if (n < limit)
                foo(n+1,limit);
            else
                throw new Exception("StackOverflow@finally("+n+")");
        }
    }
}

The output of this little pointless pile of goo is the following, and the actual exception caught may come as a surprise; 这个小小的无知堆的输出如下,实际的例外可能会让人感到惊讶; Oh, and 32 try-calls (2^5), which is entirely expected: 哦,还有32次试用(2 ^ 5),完全可以预料到:

1 - Try
2 - Try
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
2 - Finally
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
1 - Finally
2 - Try
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
2 - Finally
3 - Try
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
3 - Finally
4 - Try
5 - Try
5 - Finally
4 - Finally
5 - Try
5 - Finally
java.lang.Exception: StackOverflow@finally(5)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值