让我们将栈底索引为 0 和后续调用(因为它是递归的)在栈级别 1、2、3 等等。每次调用 workHard 都会将堆栈索引增加 1,并且每次返回应该将堆栈长度减少 1。
由于没有任何 return 语句,这段代码形成了一个无限递归,退出这个递归循环的唯一方法是一个堆栈溢出异常,打破了这个循环。
考虑修改后的代码段,不用 try/finally:
public class Workout {
public static void main(String[] args) {
workHard();
System.out.println("It’s nap time.");
}
private static void workHard() {
workHard();
}
}
在上面的代码中,假设您在堆栈的第 n 个索引处遇到异常。异常被抛出到没有任何方法处理异常的第 n-1 个堆栈,因此异常被抛出到第 n-2 个堆栈级别和第 0 级。在第 0 级得到异常后,向 main 方法抛出异常,该方法也没有任何机制来处理该异常并抛出异常。
现在来看带有 try 块的代码:
public class Workout {
public static void main(String[] args) {
workHard();
System.out.println("It’s nap time.");
}
private static void workHard() {
try {
workHard();
} finally {
workHard();
}
}
}
同样,在开始时,首先会从 try 块进行递归调用,并继续调用直到第 n 层,在第 n 层我们得到第一个堆栈溢出异常,同时对第 n + 1 层堆栈进行递归调用.
对于来自 try 块的每个堆栈溢出异常,下一行执行将是在 finally 块中调用 workHard。在第 n 级堆栈溢出异常的峰值,将捕获该异常并执行到第 n 级的 finally 块。
由于我们已经有非常有限的堆栈大小,这个 finally 块也会抛出堆栈溢出异常,并且该异常将在第 n-1 个堆栈级别被捕获。现在看到我们已经释放了一层堆栈。现在 finally 块中的调用将再次成功,并在它可能引发 stackoverflow 异常之前一直运行到第 n 级。
现在我们在第 n 个堆栈级别得到堆栈溢出异常。执行到 finally 块,该块再次引发异常,并且在第 n-1 级的 finally 块中接收到该异常。由于没有办法处理异常,所以将异常抛出到第 n-2 层堆栈,该层捕获异常并重新触发递归。
请注意,我们现在已经释放了两个堆栈级别。
现在递归级别再次到达第 n 级,控制返回到第 n-3 级,然后再次到达第 n 级,然后返回到第 n-4 级,依此类推。
在某个时间,我们将到达第 0 层堆栈的 finally 块,然后再次通过 try 进入第 n 层,然后 finally 在最终将异常抛出到 main 方法之前阻塞。
堆栈被一次又一次地释放和占用,因此有许多溢出异常和终止时间。
进入第二部分,会不会影响其他程序?
不,不应该。堆栈大小用尽了分配给 JVM 的空间。操作系统只会将有限的堆栈大小分配给 JVM。如果需要,也可以通过 JVM 参数进行控制。