Java Top.

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>查看课程

1.概述

stackoverfloweror.对Java开发人员来说可能会很讨厌,因为它是我们可以遇到的最常见的运行时错误之一。

在本文中,我们会通过查看各种代码示例以及我们如何处理它来看,我们如何如何发生这种错误。

2.堆叠框架和如何stackoverfloweror.发生

让我们从基础开始。调用方法时,在调用堆栈上创建新的堆栈帧。这个堆栈帧保存了被调用方法的参数,它的局部变量和方法的返回地址,即被调用方法返回后方法的执行应该从哪里继续。

堆栈帧的创建将继续,直到在嵌套方法中找到的方法调用结束为止。

在此过程中,如果JVM遇到没有空间创建新的堆栈帧的情况,它将抛出一个stackoverfloweror.

JVM遇到这种情况的最常见原因是未终然的/无限递归—Javadoc的描述stackoverfloweror.提到由于特定代码片段的递归过于缩短而抛出错误。

但是,递归不是此错误的唯一原因。它也可能发生在申请保留的情况下在方法中调用方法,直到堆栈耗尽。这是一种罕见的情况,因为没有开发人员会故意遵循错误的编码实践。另一个罕见的原因是在方法内具有大量局部变量

stackoverfloweror.应用程序旨在有时可以抛出C课程之间的YCLIC关系。在这种情况下,彼此的构造函数正在重复调用,这导致抛出此错误。这也可以被认为是一种递归的形式。

另一个导致此错误的有趣方案是如果是类是在与该类类的实例变量的同一类中实例化。这将导致同一类的构造函数再次调用(递归),最终导致astackoverfloweror。

在下一节中,我们将查看一些演示这些方案的代码示例。

3.stackoverfloweror.在行动中

在以下示例中,astackoverfloweror.将由于无意递归而抛出,其中开发人员忘记为递归行为指定终止条件:

公共类UnistedIneInfiniterEcursion {public int calculatefactorial(int number){return number * compulatefactorial(number-1);}}

在这里,在传递到方法的任何值的所有值都抛出错误:

公共类UnintiveIndInfiniterEcursionManualtest {@Test(预期= stackoverflower.class)public voidedpositiveintnoone_whencalfact_thenthrowsexception(){int numtocalcactorial = 1;UnIntedIndeInfiniterEcursion UIR = new untindendedinfinitereCursion();UIR.CalculdFactorial(NumTocalCactorial);@test(预期= stackoverflowerror.class)public voidedpositiveintgtone_whencalcfact_thenthrowsexception(){int numtocalcactorial = 2;UnIntedIndeInfiniterEcursion UIR = new untindendedinfinitereCursion();UIR.CalculdFactorial(NumTocalCactorial);@test(预期= stackoverflowerror.class)public void giventegationint_whencalcfact_thenthrowsexception(){int numtocalcactorial = -1;UnIntedIndeInfiniterEcursion UIR = new untindendedinfinitereCursion();UIR.CalculdFactorial(NumTocalCactorial);}}

但是,在下一个示例中,指定终止条件,但如果值-1被传递给CalculateFactorial()方法,导致未终止/无限递归:

public class InfiniteRecursionWithTerminationCondition {public int calculateFactorial(int number) {return number == 1 ?1: number * calculateFactorial(number - 1);}}

此测试集演示了此场景:

公共类InfinitoreCursionWithterMinationConditionManualTest {@Test public void给dpositiveIntnoone_whencalcfact_thencorRectlycalc(){int numtocalcactorial = 1;InfiniterEcursionWithterMinationCondition Irtc = new InfiniterEcursionWithterMinationCondition();assertequals(1,Irtc.CalculdFactorial(NumTocalCactorial));@test public void给dpositiveintgtone_whencalcfact_thencorRectlycalc(){int numtocalcactorial = 5;InfiniterEcursionWithterMinationCondition Irtc = new InfiniterEcursionWithterMinationCondition();assertequals(120,irtc.calculdfured(numtocalcactorial));@test(预期= stackoverflowerror.class)public void giventegationint_whencalcfact_thenthrowsexception(){int numtocalcactorial = -1;InfiniterEcursionWithterMinationCondition Irtc = new InfiniterEcursionWithterMinationCondition();irtc.calculdFutactorial(NumTocalCactorial);}}

在这种特殊情况下,如果终止条件简单地作为:

公共类递归rithcorrectterminationcondition {public int calculatefactorial(int number){return number <= 1?1: number * calculateFactorial(number - 1);}}

这是在实践中显示这种情况的测试:

public class RecursionWithCorrectTerminationConditionManualTest {@Test public void givennegativeint_whencalcfact_thencreclycalc () {int numToCalcFactorial = -1;rtc = new RecursionWithCorrectTerminationCondition();assertequal (rctc.calculateFactorial (numToCalcFactorial));}}

现在让我们看一个场景,其中stackoverfloweror.由于类之间的循环关系而发生。让我们考虑一下ClassOneClasstwo.,它在其构造函数内实例化彼此导致循环关系:

公共类ClassOne {Private Int OneValue;私有Classtwo clstwoinstance = null;public classOne(){OneValue = 0;clstwoinstance = new classtwo();公共级别(intonvalue,classtwo clstwoinstance){this.onevalue = OneValue;this.clstwoinstance = clstwoinstance;}}
公共类classtwo {私人int twovalue;私人ClassOne Clsoneinstance = null;公共Classtwo(){twovalue = 10;clsoneinstance = new classone();公共Classtwo(int twoverue,classone clsoneinstance){this.twovalue = twovalue;this.clsoneinstance = clsoneinstance;}}

现在让我们说我们试图实例化ClassOne如本试验所示:

public void wheninstantiationingclassone_thenthrowsexception () {ClassOne obj = new ClassOne();}}

这最终有一个stackoverfloweror.自构造函数以来ClassOne是实例化Classtwo,和建设者的Classtwo.再次是实例化的ClassOne。这反复发生,直到它溢出堆栈。

接下来,我们将在作为该类的实例变量的同一类中实例化时,查看当类在同一类中时会发生什么。

如下例所示,AccountHolder将自己实例化为实例变量ConnectAccountholder

public class AccountHolder {private String firstName;私人字符串姓;jointAccountHolder = new AccountHolder();}

当。。。的时候AccountHolder类是实例化的一种stackoverfloweror.由于该测试中看到的构造函数的递归调用,被抛出:

公共类Accounttholdermanualtest {@test(预期= stackoverflowerror.class)whenstanciatingachyountholder_thenthrowsexception(){accounttholder holder = new accountholder();}}

4.处理stackoverfloweror.

一个最好的事情stackoverfloweror.遇到的是谨慎检查堆栈迹线,以识别行号的重复模式。这将使我们能够找到具有问题递归的代码。

让我们检查一下由前面看到的代码示例引起的一些堆栈跟踪。

此堆栈跟踪由InfiniterEcursionWithterMinationConditionManualtest.如果我们省略了预期的例外声明:

java.lang.StackOverflowError at c.b.s.InfiniteRecursionWithTerminationCondition .calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5) at c.b.s.InfiniteRecursionWithTerminationCondition .calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5) at c.b.s.InfiniteRecursionWithTerminationCondition .calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5) at c.b.s.InfiniteRecursionWithTerminationCondition .calculateFactorial(InfiniteRecursionWithTerminationCondition.java:5)

在这里,可以看到行号5重复。这是递归调用正在进行的地方。现在,它只是一个检查代码以查看递归是否以正确的方式完成的问题。

这是我们通过执行的堆栈跟踪CyclicDependancanMualtest.(再一次,没有预期的例外):

java.lang.StackOverflowError at c.b.s.ClassTwo.(ClassTwo.java:9) at c.b.s.ClassOne.(ClassOne.java:9) at c.b.s.ClassTwo.(ClassTwo.java:9) at c.b.s.ClassOne.(ClassOne.java:9)

此堆栈跟踪显示导致循环关系的两个类中出现问题的行号。第9行Classtwo.和第9行ClassOne指向构造函数内的位置,它试图实例化另一类。

一旦彻底检查了代码,如果以下(或任何其他代码逻辑错误)都不是错误的原因:

  • 不正确地实施递归(即没有终止条件)
  • 课程之间的循环依赖
  • 将同一类中的类实例化为该类的实例变量

尝试增加堆栈大小是一个好主意。根据安装的JVM,默认堆栈大小可能会有所不同。

-XS.标志可用于从项目的配置或命令行增加堆栈的大小。

结论

在本文中,我们仔细看看了stackoverfloweror.包括Java代码如何导致它以及我们如何诊断和修复它。

可以找到与本文相关的源代码在GitHub

Java底部

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>查看课程
本文评论关闭!