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

Posted

技术标签:

【中文标题】递归过程中的第一个过程调用发生堆栈溢出【英文标题】:Stack overflow occurring on first procedure call inside recursive procedure 【发布时间】:2014-04-15 08:53:48 【问题描述】:

由于我理解的一个错误,我收到堆栈溢出错误,但我不明白为什么堆栈溢出发生在递归过程的第一个过程而不是调用递归过程时。

在解决数独难题的方法中,这里是递归段(粗体文本是递归调用:


    System.out.print(""); &lt= stack overflow occurs here
    int[] move_quality_sorted_keys = Sorting_jsc.RadixSort_unsigned_1( move_quality );
    for( int xPossibleMove = 1; xPossibleMove <= ctPossibleMoves; xPossibleMove++ )
        int xMove = move_quality_sorted_keys[ctPossibleMoves - xPossibleMove + 1];
        int[][] new_grid = new int[10][10];
        for( int xRow = 1; xRow <= 9; xRow++ )
            for( int xColumn = 1; xColumn <= 9; xColumn++ )
                new_grid[xRow][xColumn] = grid[xRow][xColumn];
        new_grid[move_row[xMove]][move_column[xMove]] = move_value[xMove];
        int[][] solution = solveSudokuGrid( new_grid );
        if( solution != null ) return solution;
    

堆栈溢出错误如下(注意它发生在 System.out.print() 语句中):

Exception in thread "main" java.lang.***Error
    at java.io.BufferedWriter.write(BufferedWriter.java:221)
    at java.io.Writer.write(Writer.java:157)
    at java.io.PrintStream.write(PrintStream.java:525)
    at java.io.PrintStream.print(PrintStream.java:669)
    at Euler100.solveSudokuGrid(Euler100.java:2458)
    at Euler100.solveSudokuGrid(Euler100.java:2467)
    at Euler100.solveSudokuGrid(Euler100.java:2467)
    at Euler100.solveSudokuGrid(Euler100.java:2467)
    at Euler100.solveSudokuGrid(Euler100.java:2467)
    at Euler100.solveSudokuGrid(Euler100.java:2467)
    at Euler100.solveSudokuGrid(Euler100.java:2467)
    at Euler100.solveSudokuGrid(Euler100.java:2467)
    at Euler100.solveSudokuGrid(Euler100.java:2467)
    at Euler100.solveSudokuGrid(Euler100.java:2467)

我希望在调用 solveSudokuGrid 时发生堆栈溢出,而不是在 print 语句中。为什么会这样?

【问题讨论】:

你到底在打印什么? @AndyFaizan 没什么,打印语句只是用来证明堆栈溢出发生在递归过程中的第一次调用时,而不是在调用递归过程本身时发生。 堆栈爆炸时有多深?当堆栈变得太大而无法进一步扩展时会报告该错误。如果您的例程中的第一条语句是打印调用(进一步扩展堆栈),那么就会检测到错误。 【参考方案1】:

这样看:每次调用 System.out.println 时,您都会将 4 个(或更多)额外的堆栈帧推入堆栈顶部,正如您在错误中看到的那样。然后在递归调用自己的函数之前将它们从堆栈中弹出。因此堆栈的深度是这样的:

您的代码,1 级 println,5 级 您的代码,2 级 println,6 级 您的代码,3 个级别 println,7 级 ... 您的代码,n 个级别 println,n + 4 个级别 您的代码,n + 1 级 ...

假设每个级别占用相同数量的堆栈内存(这实际上并不正确,但对于这种分析可能已经足够接近),对于堆栈大小的任何特定限制,println 应该是非常明显的代码将首先突破它。

实际上需要的只是其他过程在堆栈上使用比您的过程更多的内存,并且这种情况总是会发生。如果它使用较少,它可能仍然会发生(因为对于任何给定级别,它在您的代码之前被调用),并且大概因为 println 调用只是为了证明这一点,您在下一行调用的基数排序代码是之前触发的行为。它可能比您自己的方法使用更多的堆栈空间(这似乎很可能;您只有 6 个局部变量,并且您的大多数表达式都非常简单)。

【讨论】:

是的,除非solveSudokuGrid 的一帧所需的堆栈量大于print 调用的 4 个(或更多)方法所需的堆栈量(不太可能),否则失败将始终在print 调用中检测到。【参考方案2】:

因为此时您超出了堆栈范围。 wiki here

看来System.out.println最终会调用BufferedWriter.write,这也是一个递归函数,最终会导致***。

【讨论】:

以上是关于递归过程中的第一个过程调用发生堆栈溢出的主要内容,如果未能解决你的问题,请参考以下文章

C#:递归方法期间的堆栈溢出异常

为啥增加递归深度会导致堆栈溢出错误?

如何解决递归调用中的堆栈溢出错误?

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

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

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