为什么调用setImmediate两次导致内存使用到天空火箭?

时间:2015-11-07 21:49:15

标签: node.js

我正在尝试使用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();

您会注意到第二个示例中的内存使用量增长非常快。

3 个答案:

答案 0 :(得分:0)

在第一个doRecursiveThings个队列中,每个队列只有一个但在第二个队列中排队两个doRecursiveThings

这导致非收敛的一系列加倍:1,2,4,8,16,32,64 ......也被称为2的幂。

它也很像所谓的fork bomb

答案 1 :(得分:0)

我想这是contextscope 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亿个事物,例如,耗尽所有内存。