繁体   English   中英

Java StackOverflowError 长时间运行程序时递归导致

[英]Java StackOverflowError caused by recursion when running program for long periods of time

我正在开展一个项目,尝试创建一个神经网络,该网络将学习如何使用 NEAT 玩跳棋。 在我的跳棋游戏中,我使用递归来查找特定棋子可以进行的所有可用移动。 正常运行程序,效果很好。

问题是当我运行试图训练神经网络的程序部分时。 在我的训练计划中,我运行了无数的跳棋游戏(10000+)来尝试进化我的神经网络。 训练在前一千场左右的比赛中效果很好,但后来我遇到了一个计算器溢出错误,这是由检查可用动作的程序的递归部分引起的。 这对我来说毫无意义,因为该方法适用于前一千场左右的游戏,但它最终总是会因 stackoverflow 错误而崩溃。

编辑:这是递归方法的主要概述,我删除了很多 if 语句。 另外,我为这篇文章的长度道歉,我可能本可以以更易读和更有效的方式实现。

 private void checkAvailableTilesRecursion(GameBoardTile oldTile, LegalMove newMove) {

    ArrayList<LegalMove> recursiveCheck = new ArrayList<>();

    // Find available pieces if piece is king
    if (!edgePiece) {
        // Code to get the different tiles adjacent to this tile

        if (legalMoveCheckerPiece.getIsKing()) {
            // Up right
            // If the tile up right is clear
                LegalMove move = new LegalMove(newMove.getNewTile(), board.getTile()[newMove.returnNewY() - 2][newMove.returnNewX() + 2], newMove, null, upRight, MoveDirections.UP_RIGHT);
                newMove.setMoveAfter(move);
                availableLegalMoves.add(move); // defined elsewhere
                recursiveCheck.add(move);
            }
            // Up left
            // If the tile up left is clear
                LegalMove move = new LegalMove(newMove.getNewTile(), board.getTile()[newMove.returnNewY() - 2][newMove.returnNewX() - 2], newMove, null, upLeft, MoveDirections.UP_LEFT);
                newMove.setMoveAfter(move);
                availableLegalMoves.add(move); // defined elsewhere
                recursiveCheckRecursive.add(move);
            }

            // Down right
            // If tile down right is clear
                LegalMove move = new LegalMove(newMove.getNewTile(), board.getTile()[newMove.returnNewY() + 2][newMove.returnNewX() + 2], newMove, null, downRight, MoveDirections.DOWN_RIGHT);
                newMove.setMoveAfter(move);
                availableLegalMoves.add(move); // defined elsewhere
                recursiveCheckRecursive.add(move);
            }

            //Down left
            // If tile down left is clear
                LegalMove move = new LegalMove(newMove.getNewTile(), board.getTile()[newMove.returnNewY() + 2][newMove.returnNewX() - 2], newMove, null, downLeft, MoveDirections.DOWN_LEFT);
                newMove.setMoveAfter(move);
                availableLegalMoves.add(move); // defined elsewhere 
                recursiveCheckRecursive.add(move);
            }

        } else {

            // Find available tiles for normal pieces
            if (legalMoveCheckerPiece.getColor() == PieceColors.BLUE) {

                // Up right
                // If tile up right is clear
                    LegalMove move = new LegalMove(newMove.getNewTile(), board.getTile()[newMove.returnNewY() - 2][newMove.returnNewX() + 2], newMove, null, upRight, MoveDirections.UP_RIGHT);
                    newMove.setMoveAfter(move);
                    availableLegalMoves.add(move);
                    recursiveCheckRecursive.add(move);
                }
                // Up left
                // If tile up left is clear
                    LegalMove move = new LegalMove(newMove.getNewTile(), board.getTile()[newMove.returnNewY() - 2][newMove.returnNewX() - 2], newMove, null, upLeft, MoveDirections.UP_LEFT);
                    newMove.setMoveAfter(move);
                    availableLegalMoves.add(move);
                    recursiveCheckRecursive.add(move);
                }

            } else {
                // Red Team
                // Down right
                // If tile down right is clear
                    LegalMove move = new LegalMove(newMove.getNewTile(), board.getTile()[newMove.returnNewY() + 2][newMove.returnNewX() + 2], newMove, null, downRight, MoveDirections.DOWN_RIGHT);
                    newMove.setMoveAfter(move);
                    availableLegalMoves.add(move);
                    recursiveCheckRecursive.add(move);
                }

                //Down left
                // If tile down left is clear
                    LegalMove move = new LegalMove(newMove.getNewTile(), board.getTile()[newMove.returnNewY() + 2][newMove.returnNewX() - 2], newMove, null, downLeft, MoveDirections.DOWN_LEFT);
                    newMove.setMoveAfter(move);
                    availableLegalMoves.add(move);
                    recursiveCheckRecursive.add(move);
                }
            }
        }
    }

    if (recursiveCheckRecursive.size() > 0) {
        for (LegalMove moveToCheck : recursiveCheckRecursive) {
            checkAvailableTilesRecursion(newMove.getNewTile(), moveToCheck);
        }
    }
}

编辑#2:我认为这与内存泄漏有关。 我使用的是 Intellij 调试工具,Intellij 内存分析器显示了这一点。 内存泄漏

为什么垃圾收集器在我使用完数组列表和 LegalMove 对象后不销毁它们?

线程堆栈在 JVM 中是有限的,可以通过-Xss选项进行配置。 提供堆栈大小的另一种方法是手动创建Thread在构造函数中指定它。

如果没有这些替代方案,您可以考虑使用Trampoline 模式而不是递归实现,以便不受任何限制的影响。

没有任何代码,很难给出具体的答案。 但是,一些临时建议是:

  • (从队长明显的类型建议开始)如果您还没有使用 -Xss 选项为其提供更多堆栈内存。

  • 尝试通过限制方法范围内局部变量的数量来限制它占用的堆栈空间量,通过尝试确保您主要在堆栈内存和堆上的大多数对象对象中有引用。

  • 将其重写为迭代而不是递归;)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM