有这段代码:
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
?
答案 0 :(得分: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
因此,它有stackerror
次6869次(不同运行的更改)并打印出最后一个值。如果您只是像之前那样打印y
,那么输出可能会被缓冲,而不会被刷新,因为它不是println
。
System.out.println
在内部调用缓冲的PrintStream
。您不会从缓冲区中丢失任何数据,它会在填满之后或在您明确调用flush时将所有数据写入输出(在您的情况下为终端)。
回到这个场景,它取决于填充堆栈的内部动态以及能够从doAnything()
中的catch捕获执行的打印语句数量,并且这些字符数被写入缓冲区。在主背面,它最终印有数字2
。
答案 1 :(得分:5)
我敢打赌,通过在catch块中调用print
,您强制另一个 StackOverflowError
被外部块捕获。其中一些调用没有足够的堆栈来实际写入输出流。
JLS说:
请注意,StackOverflowError可能会被方法同步抛出 由于本机方法执行而调用以及异步 或Java虚拟机资源限制。
Java SE平台允许少量但有限的执行量 在抛出异步异常之前发生。
允许上述延迟允许优化代码进行检测 并将这些异常抛到可以处理的地方 他们在遵守Java编程语言的语义的同时。一个 简单的实现可能会轮询异步异常 每个控制转移指令的点。由于程序有一个 有限大小,这提供了检测一个总延迟的界限 异步异常。
答案 2 :(得分:3)
StackOverFlowError
第一次出现时,对最后一个doAnything()
的调用被取消,控件将从最后doAnything()
返回到catch块。
但是,因为堆栈仍然几乎已满,调用System.out.print("y")
的简单事实将导致另一个StackOverflowError
,因为需要在堆栈上推送一些值然后调用该函数print()
。
因此,再次出现另一个StackOverflowError
,现在返回上一个doAnything()
的catch {}块返回;其他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)
代替print(a)
;因此,如果一切运行正常,则应在a
的每个值之后打印一个新行。
然而,当我运行它时,我得到以下结果:
X
62066206620662066206620662066206
6190
Y
17
1
这意味着已经有17次尝试运行catch块。在这些catch块执行中,9个在生成StackOverflowError之前无法打印任何内容。 7能够打印6190的值,但是在它们再次出现错误之前无法打印换行符,最后,有一个能够打印6190的值和后面的换行符;因此,最终允许其catch块在没有任何新的StackOverflowError的情况下完成,并优雅地返回调用堆栈。
当我们处理StackOverflowError时,这些数字只是一个例子,不仅在机器之间而且在执行之间变化很大,添加或删除任何类型的指令的简单事实也应该改变这些值。但是,这里看到的模式应该保持不变。
答案 3 :(得分: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
答案 4 :(得分:0)
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所说,JVM同步或异步抛出StackOverflowError
(这意味着它可能被另一个线程抛出,可能是本机线程)这就是为什么它不可能得到的错误的堆栈跟踪。没有。线程命中catch()
块的次数未定义。
答案 5 :(得分:0)
此外,Error
类型的对象不是Exceptions
个对象,它们代表异常条件。
Errors
表示不是由程序错误引起的异常情况,
通常在程序执行期间通常不会发生,例如JVM内存不足。
虽然它们共享一个共同的超类Throwable
,但意味着两者都可以被抛出,它可以
被置于catch
但通常不应被捕,因为它们代表
罕见的,难以处理的特殊情况。
答案 6 :(得分:-2)
堆栈溢出。
您只是在异常时打印,同时程序会进行溢出。
此时发生的情况取决于各个系统,内存等。
该计划的目的是什么?