27

有这个代码:

public class Main {
    public static void main(final String[] args) throws Exception {
        System.out.print("1");
        doAnything();
        System.out.println("2");
    }

    private static void doAnything() {
        try {
            doAnything();
        } catch (final Error e) {
            System.out.print("y");
        }
    }
}

还有输出:

1yyyyyyyy2

为什么它打印“y”八次而不再打印。println()遇到Java怎么调用StackOverflowError

4

7 回答 7

13

在这里你正在捕捉Error,而不是Exception在这种情况下你的程序会崩溃。

如果您尝试此代码(修改为添加静态计数器)

public class StackError {

static int i = 1;

public static void main(final String[] args) throws Exception {
    System.out.print("1");
    doAnything();
    System.out.println("2");
}

private static void doAnything() {
    try {
        i++;
//          System.out.println(i);
        doAnything();
    } catch (Error e) {
        System.out.print("y"+i+"-");

    }
}
}

输出

 1y6869-2

因此,它有stackerror6869 次(不同运行的变化)并打印了最后一个值。如果您只是y像之前那样打印,那么输出可能会被缓冲而不被刷新,因为它不是println.


更新

内部System.out.println调用PrintStream缓冲的。您不会从缓冲区中丢失任何数据,它会在它填满后全部写入输出(在您的情况下为终端),或者当您明确调用它时刷新。

回到这个场景,这取决于堆栈被填满多少的内部动态,以及能够从 catch in 执行多少打印语句doAnything()以及将这些字符数写入缓冲区。在主背面,它最终印有数字2

javadoc 对缓冲流的引用

于 2013-02-26T07:23:42.910 回答
5

我敢打赌,通过调用printcatch 块,您可以强制另一个 StackOverflowError被外部块捕获的块。其中一些调用没有足够的堆栈来实际写入输出流。

JLS说:

请注意,StackOverflowError 可能由方法调用同步抛出,也可能由于本机方法执行或 Java 虚拟机资源限制而异步抛出。

Java SE 平台允许在抛出异步异常之前发生少量但有限的执行。

允许上面提到的延迟允许优化的代码在遵守 Java 编程语言语义的同时在实际处理它们的地方检测和抛出这些异常。一个简单的实现可能会在每个控制传输指令的点轮询异步异常。由于程序的大小是有限的,这为检测异步异常的总延迟提供了一个界限。

于 2013-02-26T07:32:25.940 回答
3

第一次StackOverFlowError发生时,对 last 的调用doAnything()被取消,控制从 last 返回到 catch 块doAnything()

然而,由于堆栈实际上仍然是满的,调用的简单事实System.out.print("y")将导致另一个StackOverflowError原因,因为需要将一些值压入堆栈然后调用函数print()

因此,另一个StackOverflowError再次发生,并且返回现在在前一个的 catch{} 块上返回doAnything();另一个StackOverflowError会发生,因为执行单个调用所需的堆栈空间需要System.out.println("y")大于从返回调用释放的空间量doAnything()

只有当堆栈上有足够的空间来执行调用时System.out.print("y"),该进程才会停止并且catch 块将成功完成。我们可以通过运行以下代码看到这一点:

public class Principal3b {

    static int a = 0;
    static int i = 0;
    static int j = 0;

    public static void main(String[] args) {
      System.out.println("X");
        doAnything();
      System.out.println("Y");
        System.out.println(i);        
        System.out.println(j);
    }

    private static void doAnything() {

        a++;
        int b = a;

        try {
            doAnything();
        } catch (final Error e) {
            i++;
            System.out.println(a);
            System.out.println(b);
            j++;
        }
    }
}

请注意,println(a)使用 a 而不是 a print(a); a因此,如果一切正常,则应在每个值之后打印一个新行。

但是,当我运行它时,我得到以下结果:

X
62066206620662066206620662066206
6190
Y
17
1

这意味着已经有 17 次尝试运行 catch 块。在这些 catch 块执行中,有 9 个在自己生成 StackOverflowError 之前无法打印任何内容;7 能够打印 6190 的值,但在再次出现错误之前无法在其后打印换行符,最后,有一个能够同时打印 6190 的值和其后的换行符;因此最终允许它的 catch 块在没有任何新的 StackOverflowError 的情况下完成并优雅地返回调用堆栈。

当我们处理 StackOverflowError 时,这些数字只是一个示例,不仅在机器之间而且在执行之间会有很大差异,添加或删除任何类型的指令的简单事实也应该改变这些值。但是,这里看到的模式应该保持不变。

于 2013-02-27T08:33:27.997 回答
1

有一点很清楚 System.out.print("y"); 在 catch 中创建了这个谜题。如果我们将代码更改为

static int n;

public static void main(final String[] args) throws Exception {
    System.out.println("1");
    doAnything();
    System.out.println(n);
}

private static void doAnything() {
    try {
        doAnything();
    } catch (Error e) {
        n++;
    }
}

它打印

1
1
于 2013-02-26T07:47:30.993 回答
0

好吧,没有。命中堆栈溢出错误的次数是未定义的。但是,JVM 允许您从StackOverflowError错误中恢复并继续正常执行系统。

由以下代码证明:

public class Really {
   public static void main(final String[] args) throws Exception {
     System.out.print("1");
     doAnything();
     System.out.println("2");
   }
   private static void doAnything() {
    try {
           throw new StackOverflowError();
       //doAnything();
    }
    catch(final Error e){
        System.out.print("y");
    }
   }
}

但是请注意,正如@Javier 所说,StackOverflowErrorJVM 同步或异步抛出 (这意味着它可以由另一个线程,可能是本机线程)抛出,这就是为什么无法获取错误的堆栈跟踪的原因。没有。线程命中catch()块的次数是未定义的。

于 2013-02-26T07:40:34.520 回答
0

此外,类型Error的对象不是Exceptions它们代表异常情况的对象。 Errors表示非程序错误引起的异常情况,通常在程序执行过程中不会发生,例如JVM内存不足。尽管它们共享一个共同的超类Throwable,这意味着两者都可以被抛出,但它可以被放置在 acatch但通常不应该被捕获,因为它们代表了罕见的、难以处理的异常情况。

于 2013-03-01T07:33:50.070 回答
-2

堆栈溢出。

您只打印异常,同时程序递归到溢出。

在什么时候发生这种情况取决于各个系统、内存等。

该计划的目的是什么?

于 2013-02-26T07:11:32.123 回答