简体   繁体   English

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

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

I am working on a project to try and create a neural net that will learn how to play checkers using NEAT.我正在开展一个项目,尝试创建一个神经网络,该网络将学习如何使用 NEAT 玩跳棋。 In my checkers game, I use recursion to find all the available moves a specific piece can make.在我的跳棋游戏中,我使用递归来查找特定棋子可以进行的所有可用移动。 Running the program normally, it works great.正常运行程序,效果很好。

The problem is when I run the part of the program that tries to train the neural network.问题是当我运行试图训练神经网络的程序部分时。 In my training program, I run countless checkers games (10000+) to try to evolve my neural network.在我的训练计划中,我运行了无数的跳棋游戏(10000+)来尝试进化我的神经网络。 Training works great for the first thousand games or so, but then I get hit with a stackoverflow error that is caused from the recursion part of the program that checks for available moves.训练在前一千场左右的比赛中效果很好,但后来我遇到了一个计算器溢出错误,这是由检查可用动作的程序的递归部分引起的。 This makes no sense to me as the method works fine for the first thousand games or so, but it always eventually breaks down with a stackoverflow error.这对我来说毫无意义,因为该方法适用于前一千场左右的游戏,但它最终总是会因 stackoverflow 错误而崩溃。

EDIT: Here is the main overview of the recursive method I cut out a lot of the if statements.编辑:这是递归方法的主要概述,我删除了很多 if 语句。 Also, I apologize for the length of this, I probably could have implemented in a more readable and efficient way.另外,我为这篇文章的长度道歉,我可能本可以以更易读和更有效的方式实现。

 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);
        }
    }
}

EDIT #2: I think this has to do something with a memory leak.编辑#2:我认为这与内存泄漏有关。 I was using the Intellij Debug tool and the Intellij Memory Analyzer showed this.我使用的是 Intellij 调试工具,Intellij 内存分析器显示了这一点。 内存泄漏

Why wouldn't the garbage collector destroy the arraylists and LegalMove objects after I am done using them though?为什么垃圾收集器在我使用完数组列表和 LegalMove 对象后不销毁它们?

The thread stack is limited in the JVM and can be configured via the -Xss option.线程堆栈在 JVM 中是有限的,可以通过-Xss选项进行配置。 Another way to provide the stack size is by specifying it in the constructor when creating Thread s by hand.提供堆栈大小的另一种方法是手动创建Thread在构造函数中指定它。

If these alternatives are no option, you might consider using the Trampoline pattern instead of a recursive implementation in order to be independent of any limitation.如果没有这些替代方案,您可以考虑使用Trampoline 模式而不是递归实现,以便不受任何限制的影响。

Without any code, its hard to give a concrete answer.没有任何代码,很难给出具体的答案。 But, a couple of offhand suggestions would be:但是,一些临时建议是:

  • (Starting with the captain obvious type suggestion) If you haven't already give it more stack memory using the -Xss option. (从队长明显的类型建议开始)如果您还没有使用 -Xss 选项为其提供更多堆栈内存。

  • try and limit the amount of stack space it occupies by limiting the amount of local variables within the method scope, by trying to ensure you mostly have references in the stack memory and most of your objects objects on the heap.尝试通过限制方法范围内局部变量的数量来限制它占用的堆栈空间量,通过尝试确保您主要在堆栈内存和堆上的大多数对象对象中有引用。

  • rewrite it to be iterative instead of recursive ;)将其重写为迭代而不是递归;)

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

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