由于递归方法调用导致 Java 堆栈溢出

Posted

技术标签:

【中文标题】由于递归方法调用导致 Java 堆栈溢出【英文标题】:Java stack overflow because of recursive method call 【发布时间】:2012-07-24 09:02:50 【问题描述】:

我正在使用 Java 完成一项学校作业,但遇到了一个错误,我无法找到答案。 不知何故,当我在iterator.next() 返回的对象上调用gethit() 方法时,我得到一个堆栈溢出异常。 我怀疑是因为 gethit() 方法(在这种特定情况下)递归地调用自身。尽管如此,我认为堆栈溢出很奇怪,因为递归只有 2 或 3 级深,而且我的对象不会使用过多的内存。

第一次调用gethit()shoot()方法

public void shoot() 
    assert canHaveAsEnergy(energy - 1000);

    //Search the target position.
    Position laserPos = new Position(getPos().getX(), getPos().getY(), getPos().getBoard());
    do 
        long nextX = laserPos.getX() + new Double(orientation.getDirection().getX()).longValue();
        long nextY = laserPos.getY() + new Double(orientation.getDirection().getY()).longValue();
        laserPos.setX(nextX);
        laserPos.setY(nextY);
     while (getPos().getBoard().canHaveAsPosition(laserPos) && (! getPos().getBoard().hasAsPosition(laserPos)));
    //Hit every entity on the target position. 
    for (Entity entity : getPos().getBoard().getAllEntitiesOn(laserPos)) 
        entity.getHit();
    
    setEnergy(energy - 1000);

递归调用自身的getHit() 方法。

public void getHit() 
    ArrayList<Position> neighbours = new ArrayList<Position>();
    Position northPos = new Position(getPos().getX(), getPos().getY() - 1, getPos().getBoard());
    Position eastPos = new Position(getPos().getX() + 1, getPos().getY(), getPos().getBoard());
    Position southPos = new Position(getPos().getX(), getPos().getY() + 1, getPos().getBoard());
    Position westPos = new Position(getPos().getX() - 1, getPos().getY(), getPos().getBoard());
    neighbours.add(northPos);
    neighbours.add(eastPos);
    neighbours.add(southPos);
    neighbours.add(westPos);

    for (Position pos : neighbours) 
        if (getPos().getBoard().hasAsPosition(pos)) 
            Iterator<Entity> iterator = getPos().getBoard().getAllEntitiesOn(pos).iterator();
            while (iterator.hasNext()) 
                //Somehow this gives a stack overflow error
                iterator.next().getHit();
            
               
    
    System.out.println(this.toString() + " takes a hit and explodes.");
    getPos().getBoard().removeAsEntity(this);
    terminate();

【问题讨论】:

“递归只进行 2 或 3 层深度” - 你确定对此有信心吗? 【参考方案1】:

每次调用迭代器时,都会调用另一个迭代器,然后再调用另一个迭代器,以此类推。因此,由于每个迭代器调用,您的堆栈从无限递归中溢出

iterator.next().gethit();

每个迭代器只会创建一个需要通过的新迭代器,但您会一次又一次地调用 getHit(),因此您永远无法完成任何函数调用。

【讨论】:

【参考方案2】:

iterator.next().getHit();调用 getHit() 方法并再次开始迭代并继续(递归循环)。有一个变量或终止点来退出递归循环。

每当调用方法时,它都会将信息推送到堆栈帧,在方法完成时堆栈帧将被删除。在您的情况下,无法完成方法完成和删除堆栈帧,这会生成 ***Error

【讨论】:

【参考方案3】:

在实现递归时,您应该确保有一个终止调用,该方法不会调用自身。

现在你假设当你移动到邻居并检查他们是否被击中但当你看到调用时,这个递归应该停止...... (这是初始位置为 2,2 的空跑)

[Original]=>[P1],[P2],[P3],[P4]
**[2,2]**=>[2,1],[3,2],[2,3],[1,2]
[2,1]=>[2,0],[3,1],**[2,2]**,[1,1]
[3,2]=>[3,1],[4,2],[3,3],[2,2]
[2,3]=>[2,2],[3,3],[2,4],[1,3]
[1,2]=>[1,1],[2,2],[1,3],[0,2]

因此,当您首先计算 4 个邻居并在其上调用 getHit() 时。 源单元格是任何一个邻居的邻居,这足以进入无休止的递归。

您可以通过输入以下语句来确定您的价值观...

public void getHit() 
    System.out.println("[" + getPos().getX() + "," + getPos().getY() + "]");
    ....

这里的解决方案是保留一个单元格列表,将其作为参数传递,这些单元格被访问过,并且不再访问它们。希望这会有所帮助。

【讨论】:

以上是关于由于递归方法调用导致 Java 堆栈溢出的主要内容,如果未能解决你的问题,请参考以下文章

Java如何在不使用递归的情况下导致栈溢出?

递归过程中的第一个过程调用发生堆栈溢出

递归调用太深,可能导致栈溢出

Java 中的自调用函数中的堆栈溢出错误(岛数)

如何启用递归函数以避免堆栈溢出?

如何解决栈溢出