簡體   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