Java中的StackOverflowError错误

StackOverflowError 可能会让JAVA开发人员感到恼火 , 因为它是我们可能遇到的最常见的运行时错误之一 。
在本文中 , 我们将通过查看各种代码示例以及如何处理它来了解此错误是如何发生的 。
Stack Frames和StackOverflowerError的发生方式【Java中的StackOverflowError错误】让我们从基础的开始 。调用方法时 , 将在调用堆栈上创建新的堆栈帧(stack frame) 。该堆栈框架包含被调用方法的参数、其局部变量和方法的返回地址 , 即在被调用方法返回后应继续执行方法的点 。
堆栈帧的创建将继续 , 直到到达嵌套方法中的方法调用结束 。
在此过程中 , 如果JVM遇到没有空间创建新堆栈帧的情况 , 它将抛出 StackOverflower 错误 。
JVM遇到这种情况的最常见原因是未终止/无限递归——StackOverflowerr的Javadoc描述提到 , 错误是由于特定代码段中的递归太深而引发的 。
然而 , 递归并不是导致此错误的唯一原因 。在应用程序不断从方法内调用方法直到堆栈耗尽的情况下 , 也可能发生这种情况 。这是一种罕见的情况 , 因为没有开发人员会故意遵循糟糕的编码实践 。另一个罕见的原因是方法中有大量的局部变量 。
当应用程序设计为类之间具有循环关系时 , 也可以抛出StackOverflowError 。在这种情况下 , 会重复调用彼此的构造函数 , 从而引发此错误 。这也可以被视为递归的一种形式 。
另一个引起此错误的有趣场景是 , 如果一个类在同一个类中作为该类的实例变量实例化 。这将导致一次又一次(递归)调用同一类的构造函数 , 最终导致堆栈溢出错误 。
StackOverflowerError正在运行在下面所示的示例中 , 由于意外递归 , 开发人员忘记为递归行为指定终止条件 , 将抛出StackOverflowError错误:
public class UnintendedInfiniteRecursion {public int calculateFactorial(int number) {return number * calculateFactorial(number - 1);}}在这里 , 对于传递到方法中的任何知识 , 在任何情况下都会引发错误:
public class UnintendedInfiniteRecursionManualTest {@Test(expected = <a href=https://www.isolves.com/it/cxkf/yy/JAVA/2022-07-14/"https://javakk.com/tag/stackoverflowerror" title="查看更多关于 StackOverflowError 的文章" target="_blank">StackOverflowError.class)public void givenPositiveIntNoOne_whenCalFact_thenThrowsException() {int numToCalcFactorial= 1;UnintendedInfiniteRecursion uir= new UnintendedInfiniteRecursion();uir.calculateFactorial(numToCalcFactorial);}@Test(expected = StackOverflowError.class)public void givenPositiveIntGtOne_whenCalcFact_thenThrowsException() {int numToCalcFactorial= 2;UnintendedInfiniteRecursion uir= new UnintendedInfiniteRecursion();uir.calculateFactorial(numToCalcFactorial);}@Test(expected = StackOverflowError.class)public void givenNegativeInt_whenCalcFact_thenThrowsException() {int numToCalcFactorial= -1;UnintendedInfiniteRecursion uir= new UnintendedInfiniteRecursion();uir.calculateFactorial(numToCalcFactorial);}}但是 , 在下一个示例中 , 指定了终止条件 , 但如果将值 -1 传递给 calculateFactorial() 方法 , 则永远不会满足终止条件 , 这会导致未终止/无限递归:
public class InfiniteRecursionWithTerminationCondition {public int calculateFactorial(int number) {return number == 1 ? 1 : number * calculateFactorial(number - 1);}}这组测试演示了此场景:
public class InfiniteRecursionWithTerminationConditionManualTest {@Testpublic void givenPositiveIntNoOne_whenCalcFact_thenCorrectlyCalc() {int numToCalcFactorial = 1;InfiniteRecursionWithTerminationCondition irtc= new InfiniteRecursionWithTerminationCondition();assertEquals(1, irtc.calculateFactorial(numToCalcFactorial));}@Testpublic void givenPositiveIntGtOne_whenCalcFact_thenCorrectlyCalc() {int numToCalcFactorial = 5;InfiniteRecursionWithTerminationCondition irtc= new InfiniteRecursionWithTerminationCondition();assertEquals(120, irtc.calculateFactorial(numToCalcFactorial));}@Test(expected = StackOverflowError.class)public void givenNegativeInt_whenCalcFact_thenThrowsException() {int numToCalcFactorial = -1;InfiniteRecursionWithTerminationCondition irtc= new InfiniteRecursionWithTerminationCondition();irtc.calculateFactorial(numToCalcFactorial);}}在这种特殊情况下 , 如果将终止条件简单地表示为:
public class RecursionWithCorrectTerminationCondition {public int calculateFactorial(int number) {return number <= 1 ? 1 : number * calculateFactorial(number - 1);}}


推荐阅读