Node.js - 超出最大调用堆栈大小

新手上路,请多包涵

当我运行我的代码时,Node.js 会抛出一个 "RangeError: Maximum call stack size exceeded" 由太多递归调用引起的异常。我尝试将 Node.js 堆栈大小增加 sudo node --stack-size=16000 app ,但 Node.js 崩溃而没有任何错误消息。当我在没有 sudo 的情况下再次运行它时,Node.js 会打印 'Segmentation fault: 11' 。有没有可能在不删除我的递归调用的情况下解决这个问题?

原文由 user1518183 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.9k
2 个回答

您应该将递归函数调用包装成

  • setTimeout ,
  • setImmediate
  • process.nextTick

函数让 node.js 有机会清除堆栈。如果您不这样做并且有许多循环没有任何 真正的 异步函数调用,或者您不等待回调,那么您的 RangeError: Maximum call stack size exceeded 将是 不可避免 的。

有很多关于“潜在异步循环”的文章。 这是一个

现在还有一些示例代码:

 // ANTI-PATTERN
// THIS WILL CRASH

var condition = false, // potential means "maybe never"
    max = 1000000;

function potAsyncLoop( i, resume ) {
    if( i < max ) {
        if( condition ) {
            someAsyncFunc( function( err, result ) {
                potAsyncLoop( i+1, callback );
            });
        } else {
            // this will crash after some rounds with
            // "stack exceed", because control is never given back
            // to the browser
            // -> no GC and browser "dead" ... "VERY BAD"
            potAsyncLoop( i+1, resume );
        }
    } else {
        resume();
    }
}
potAsyncLoop( 0, function() {
    // code after the loop
    ...
});

这是正确的:

 var condition = false, // potential means "maybe never"
    max = 1000000;

function potAsyncLoop( i, resume ) {
    if( i < max ) {
        if( condition ) {
            someAsyncFunc( function( err, result ) {
                potAsyncLoop( i+1, callback );
            });
        } else {
            // Now the browser gets the chance to clear the stack
            // after every round by getting the control back.
            // Afterwards the loop continues
            setTimeout( function() {
                potAsyncLoop( i+1, resume );
            }, 0 );
        }
    } else {
        resume();
    }
}
potAsyncLoop( 0, function() {
    // code after the loop
    ...
});

现在您的循环可能会变得太慢,因为我们每轮都会浪费一点时间(一次浏览器往返)。但是您不必在每一轮中都调用 setTimeout 。通常每 1000 次执行一次是可以的。但这可能会因您的堆栈大小而异:

 var condition = false, // potential means "maybe never"
    max = 1000000;

function potAsyncLoop( i, resume ) {
    if( i < max ) {
        if( condition ) {
            someAsyncFunc( function( err, result ) {
                potAsyncLoop( i+1, callback );
            });
        } else {
            if( i % 1000 === 0 ) {
                setTimeout( function() {
                    potAsyncLoop( i+1, resume );
                }, 0 );
            } else {
                potAsyncLoop( i+1, resume );
            }
        }
    } else {
        resume();
    }
}
potAsyncLoop( 0, function() {
    // code after the loop
    ...
});

原文由 heinob 发布,翻译遵循 CC BY-SA 3.0 许可协议

我找到了一个肮脏的解决方案:

 /bin/bash -c "ulimit -s 65500; exec /usr/local/bin/node --stack-size=65500 /path/to/app.js"

它只会增加调用堆栈限制。我认为这不适合生产代码,但对于只运行一次的脚本我需要它。

原文由 user1518183 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题