我正在尝试使用setImmediate在nodejs中实现递归函数,并且由于某种原因,该进程的内存使用量急剧上升。我已经将问题缩小到我在函数体内调用“setImmediate”两次的事实。
您可以通过比较以下内存使用情况来看到这种情况:
var numTicks = 0;
function doRecursiveThings(){
setImmediate(doRecursiveThings);
if(numTicks % 777000 == 0) {
console.log(Date.now());
}
numTicks += 1;
}
doRecursiveThings();
对战
var numTicks = 0;
function doRecursiveThings(){
setImmediate(doRecursiveThings);
if(numTicks % 777000 == 0) {
console.log(Date.now());
}
numTicks += 1;
setImmediate(doRecursiveThings);
}
doRecursiveThings();
您会注意到第二个示例中的内存使用量增长非常快。
答案 0 :(得分:0)
在第一个doRecursiveThings
个队列中,每个队列只有一个但在第二个队列中排队两个doRecursiveThings
。
这导致非收敛的一系列加倍:1,2,4,8,16,32,64 ......也被称为2的幂。
它也很像所谓的fork bomb。
答案 1 :(得分:0)
我想这是context
和scope chain
s的问题。
我的意思是,每当你以这种方式开始一个新的功能时,范围链会扩展一个级别(由于需要遍历整个问题所有问题,这不是问题的焦点),是context
本身(实际上,我认为链接是相反的,因为它是在创建背景后分配给上下文的范围)。
在第二种情况下,在N
步骤中,实际上是上下文和链的数量加倍,而新生成的那些可能共享公共部分(它取决于实际的底层实现)但是给出了诞生了两个新的完全分离和独立的对象。
因为你在函数中引入了递归,所以新生成的上下文对象的数量会爆炸,从1开始,然后是2,4,8,依此类推。在N
步骤中,您有类似2^N
新创建的对象,所有这些对象都独立于其他对象。这意味着,作为一个例子,为所有这些变量创建局部变量,因此即使您只有一个变量,实际上也是让它们的数量呈指数增长。请注意,即使您没有局部变量,也可能花费几个字节,因为链必须引用本地上下文(其中有一些东西,比如函数参数数组)以及所有父项'那些,所以你需要在任何情况下区分它们。
你的回忆谢谢你。 : - )
因为我忘记了所有事情的运作方式,而且我很确定我错过了某些内容,here是一个有助于进一步澄清疑虑的链接。
答案 2 :(得分:0)
事件循环中的内存呈指数级增长,每次运行函数doRecursiveThings
时,它会在事件循环中再添加2个内容,从而扼杀libuv中的所有内存。为了可视化事件循环,您可以这样想象:
doRecursiveThings: 1 call in event loop
doRecursiveThings: 2 calls in event loop
doRecursiveThings: 4 calls in event loop
doRecursiveThings: 8 calls in event loop
doRecursiveThings: 16 calls in event loop
doRecursiveThings: 32 calls in event loop
doRecursiveThings: 64 calls in event loop
doRecursiveThings: 128 calls in event loop
因此,通过根调用的第32次迭代,事件循环中将有大约40亿个事物,例如,耗尽所有内存。