,,Java中的StackOverflowError错误问题及解决方法

,,Java中的StackOverflowError错误问题及解决方法

本文主要介绍Java中的StackOverflowError。在本文中,我们仔细研究了堆栈溢出错误,包括Java代码如何导致它,以及我们如何诊断和修复它。有需要的朋友可以参考一下。

目录

StackOverflow Error介绍堆栈帧和StackOverflow错误如何发生Stack Overflow错误正在运行解决堆栈溢出错误结论

StackOverflowError简介

StackOverflowError可能会让Java开发人员感到烦恼,因为它是我们可能会遇到的最常见的运行时错误之一。在本文中,我们将通过查看各种代码示例来理解这个错误是如何发生的,以及如何处理它。堆栈帧和StackOverflowerError发生的方式,让我们从基础开始。当调用方法时,将在调用堆栈上创建一个新的堆栈框架。堆栈框架包含被调用方法的参数及其局部变化。

StackOverflowError可能会让Java开发人员感到烦恼,因为它是我们可能会遇到的最常见的运行时错误之一。

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

Stack Frames和StackOverflowerError的发生方式

让我们从基础开始。当调用方法时,将在调用堆栈上创建一个新的堆栈框架。这个堆栈帧包含了被调用方法的参数,它的局部变量和方法的返回地址,也就是被调用方法返回后该方法应该继续执行的点。

堆栈的创建将一直持续到嵌套方法中的方法调用结束。

在这个过程中,如果JVM遇到没有空间创建新的堆栈框架的情况,就会抛出StackOverflower错误。

JVM遇到这种情况最常见的原因是,未终止/无限递归——StackOverflowerr的Javadoc描述中提到,错误是由特定代码段中太深的递归引起的。

然而,递归并不是这个错误的唯一原因。当应用程序一直从方法内部调用方法,直到堆栈耗尽时,也会发生这种情况。这是一种罕见的情况,因为没有开发人员会故意遵循糟糕的编码实践。另一个罕见的原因是方法中有大量的局部变量。

当应用程序被设计为在类之间具有循环关系时,也会引发StackOverflowError。在这种情况下,彼此的构造函数被重复调用,就会导致这个错误。这也可以看作是递归的一种形式。

导致此错误的另一个有趣的场景是,如果一个类在同一个类中被实例化为该类的实例变量。这将导致同一类的构造函数被反复调用(递归),最终导致堆栈溢出错误。

StackOverflowerError正在运行

在下面的示例中,由于意外的递归,开发人员忘记了为递归行为指定终止条件,并将引发StackOverflowError错误:

公共类UnintendedInfiniteRecursion {

public int calculate factorial(int number){

返回数字* calculateFactorial(数字-1);

}

}

这里,对于传递给方法的任何值,在任何情况下都会引发错误:

公共类unintendedinfiniterecrusionmanualtest {

@ test(expected=a href=' 3359javakk.com/tag/StackOverflowError' rel='外部nofollow' rel='外部nofollow' title='查看更多关于stackoverflow错误的文章' target=' _ blank' stackoverflow错误/a.class)

public void givenPositiveIntNoOne _ whenCalFact _ thentthrowsexception(){

int numToCalcFactorial=1;

未定义的定义

=new unintendedinfiniterecrusion();

uir . calculate factorial(numToCalcFactorial);

}

@ Test(expected=stack overflow error . class)

public void givenpositiveington e _ whenCalcFact _ thenThrowsException(){

int numToCalcFactorial=2;

未定义的定义

=new unintendedinfiniterecrusion();

uir . calculate factorial(numToCalcFactorial);

}

@ Test(expected=stack overflow error . class)

public void givenNegativeInt _ whenCalcFact _ thenThrowsException(){

int numToCalcFactorial=-1;

未定义的定义

=new unintendedinfiniterecrusion();

uir。计算阶乘(numToCalcFactorial);

}

}

但是,在下一个示例中,指定了终止条件,但如果将值-1传递给计算阶乘()方法,则永远不会满足终止条件,这会导致未终止/无限递归:

公共类infiniterecursionwithtterminationcondition {

public int calculate阶乘(int number){

返回数==1?1:number *计算阶乘(number-1);

}

}

这组测试演示了此场景:

公共类infiniterecursionwithtterminationconditionmanualtest {

@测试

public void givenPositiveIntNoOne _ whenCalcFact _ then correctlycalc(){

int numToCalcFactorial=1;

infiniterecursionwithtterminationcondition irtc

=new infiniterecursionwithtterminationcondition();

assertEquals(1,irtc。计算阶乘(numToCalcFactorial));

}

@测试

public void given positive ington e _ when calc fact _ then correct ly calc(){

int numToCalcFactorial=5;

infiniterecursionwithtterminationcondition irtc

=new infiniterecursionwithtterminationcondition();

assertEquals(120,irtc。计算阶乘(numToCalcFactorial));

}

@ Test(预期=堆栈溢出错误。类)

public void givenNegativeInt _ whenCalcFact _ thenThrowsException(){

int numToCalcFactorial=-1;

infiniterecursionwithtterminationcondition irtc

=new infiniterecursionwithtterminationcondition();

irtc。计算阶乘(numToCalcFactorial);

}

}

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

公共类recursionwithcorrecterminationcondition {

public int calculate阶乘(int number){

退货数量=1?1:number *计算阶乘(number-1);

}

}

下面的测试在实践中显示了这种情况:

公共类recursionwithcorrecterminationconditionmanualtest {

@测试

public void givennegativint _ whenCalcFact _ then correctlycalc(){

int numToCalcFactorial=-1;

recursionwithcorrecterminationcondition rctc

=new recursionwithcorrecterminationcondition();

assertEquals(1,rctc。计算阶乘(numToCalcFactorial));

}

}

现在让我们来看一个场景,其中堆叠在花上错误错误是由于类之间的循环关系而发生的。让我们考虑一班和二班,它们在其构造函数中相互实例化,从而产生循环关系:

公共类一级{

私有整数一值

私有类二clsTwoInstance=null

public ClassOne() {

一个值=0;

clsTwoInstance=new class two();

}

public ClassOne(int oneValue,ClassTwo clsTwoInstance) {

这个。一个值=一个值;

这个。clsTwoInstance=clsTwoInstance

}

}

公共课二班{

私有整数二值

private class one clsOneInstance=null;

公共二类(){

两个值=10

clsOneInstance=new class one();

}

public ClassTwo(int twoValue,ClassOne clsOneInstance) {

这个。二值=二值;

这个。clsOneInstance=clsOneInstance

}

}

现在让我们假设我们尝试实例化一班,如本测试中所示:

公共类循环依赖手册测试{

@ Test(预期=堆栈溢出错误。类)

实例化类one _ thethrowsexception(){

class one obj=new class one();

}

}

这最终导致了堆叠在花上错误错误,因为一班的构造函数实例化了二班,而二班的构造函数再次实例化了一班。这种情况反复发生,直到它溢出堆栈。

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

如下一个示例所示,账户持有人将自身实例化为实例变量联名账户持有人:

公共类账户持有人{

私有字符串名字

私有字符串姓

account holder joint account holder=new account holder();

}

实例化AccountHolder类时,由于构造函数的递归调用,将引发StackOverflowError错误,如下测试所示:

公共类帐户持有人手册测试{

@ Test(expected=stack overflow error . class)

public void when instanciatingaccounthorder _ thenThrowsException(){

account holder holder=new account holder();

}

}

解决StackOverflowError

当遇到StackOverflowError堆栈溢出错误时,最好的办法是仔细检查堆栈跟踪,以确定行号的重复模式。这将使我们能够定位有问题递归的代码。

让我们研究几个由我们前面看到的代码示例引起的堆栈跟踪。

如果预期的异常声明被忽略,此堆栈跟踪将由infinitecurationwithttermination条件手动测试生成:

java.lang.StackOverflowError

在c . b . s . infiniterecursionwithtterminationcondition。calculate factorial(infiniterecursionwithtterminationcondition . Java:5)

在c . b . s . infiniterecursionwithtterminationcondition。calculate factorial(infiniterecursionwithtterminationcondition . Java:5)

在c . b . s . infiniterecursionwithtterminationcondition。calculate factorial(infiniterecursionwithtterminationcondition . Java:5)

在c . b . s . infiniterecursionwithtterminationcondition。calculate factorial(infiniterecursionwithtterminationcondition . Java:5)

在这里,您可以看到第5行是重复的。这是进行递归调用的地方。现在只需检查代码,看看递归是否以正确的方式完成。

下面是我们通过执行CyclicDependancyManualTest(同样,没有意外的异常)得到的堆栈跟踪:

java.lang.StackOverflowError

位于c . b . s . class two . init(class two . Java:9)

位于c . b . s . class one . init(class one . Java:9)

位于c . b . s . class two . init(class two . Java:9)

位于c . b . s . class one . init(class one . Java:9)

这个堆栈跟踪显示了导致循环关系中的两个类出现问题的行号。class 2的第9行和class 1的第9行指向构造函数中试图实例化另一个类的位置。

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

错误的递归(即没有终止条件)类之间的循环依赖将一个类实例化为其实例变量所在的同一个类。

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

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

结论

在本文中,我们仔细研究了StackOverflower错误,包括Java代码如何导致它,以及我们如何诊断和修复它。

与本文相关的源代码可以在GitHub上找到:https://GitHub . com/eu genp/tutorials/tree/master/core-Java-modules/core-Java-exceptions。

关于Java中的StackOverflowError的这篇文章到此为止。有关JavaStackOverflowError的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望你以后能支持我们!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: