node.js的垃圾收集

时间:2011-03-16 13:52:12

标签: javascript node.js v8

我很好奇嵌套函数的node.js模式如何与v8的垃圾收集器一起工作。 这是一个简单的例子

readfile("blah", function(str) {
   var val = getvaluefromstr(str);
   function restofprogram(val2) { ... } (val)
})

如果restofprogram长时间运行,那是不是意味着str永远不会被垃圾收集?我的理解是,对于节点,你最终会使用嵌套函数。如果在外部声明restofprogram,这是否会收集垃圾,因此str不在范围内?这是推荐的做法吗?

编辑我不打算让问题变得复杂。那只是粗心大意,所以我修改了它。

3 个答案:

答案 0 :(得分:66)

简单回答:如果str的值未被其他任何地方引用(且str本身未从restofprogram引用),则{{1}将立即无法访问function (str) { ... }返回。

详细信息:V8编译器将真正的本地变量与闭包捕获的所谓 context 变量区分开来,由 with -statement或eval调用。

局部变量存在于堆栈中,并在函数执行完成后立即消失。

上下文变量存在于堆分配的上下文结构中。当上下文结构消失时,它们就会消失。这里要注意的重要一点是来自同一范围的上下文变量存在于相同结构中。让我用示例代码来说明它:

function outer () {
  var x; // real local variable
  var y; // context variable, referenced by inner1
  var z; // context variable, referenced by inner2

  function inner1 () {
    // references context 
    use(y);
  }

  function inner2 () {
    // references context 
    use(z);
  }

  function inner3 () { /* I am empty but I still capture context implicitly */ } 

  return [inner1, inner2, inner3];
}

在此示例中,变量x将在outer返回后立即消失,但变量yz仅在两者 {{时才会消失1}},inner1 inner2死亡。发生这种情况是因为inner3y在同一个上下文结构中分配,并且所有三个闭包都隐式引用了这个上下文结构(即使z没有明确地使用它)。

当你开始使用 - 陈述,尝试/捕获 - 陈述时,情况变得更加复杂,这在V8上包含隐含的 - catch子句或全局inner3中的语句。

eval

在此示例中,function complication () { var x; // context variable function inner () { /* I am empty but I still capture context implicitly */ } try { } catch (e) { /* contains implicit with-statement */ } return inner; } 仅在x死亡时消失。因为:

  • 尝试/捕获 - 在catch子句中隐含 with -statement
  • V8假设 - 陈述阴影全部本地人

这会强制inner成为上下文变量,而x会捕获上下文,因此inner会一直存在x

一般情况下,如果您想确定给定变量不会保留某个对象超过实际需要的时间,您可以通过为该变量分配inner来轻松销毁此链接。

答案 1 :(得分:4)

实际上你的例子有些棘手。是故意的吗?您似乎使用内部词法范围的restofprogram()的val参数屏蔽外部val变量,而不是实际使用它。但无论如何,你问的是str所以,为了简单起见,让我在你的例子中忽略val的棘手。

我的猜测是,在restofprogram()函数完成之前,str变量不会被收集,即使它没有使用它。 如果,restofprogram()不使用str ,则不会使用eval()new Function(),那么可以安全收集,但我怀疑它会。这对于V8来说可能是一个棘手的优化,可能不值得这么麻烦。如果语言中没有evalnew Function(),则会更容易。

现在,它并不意味着它永远不会被收集,因为单线程事件循环中的任何事件处理程序几乎都应该立即完成。否则你的整个过程都会被阻止,而你在内存中遇到的问题比一个无用的变量还要大。

现在我想知道你的意思是否与你在实例中写下的内容不同。 Node中的整个程序就像在浏览器中一样 - 它只是注册在主程序体已经完成之后异步触发的事件回调。此外,没有任何处理程序阻塞,因此没有任何功能实际上花费任何明显的时间来完成。我不确定我是否理解你在问题中的实际含义,但我希望我所写的内容将有助于理解它是如何运作的。

更新

在关于程序如何看的评论中阅读更多信息后,我可以说更多。

如果你的程序是这样的:

readfile("blah", function (str) {
  var val = getvaluefromstr(str);
  // do something with val
  Server.start(function (request) {
    // do something
  });
});

然后你也可以这样写:

readfile("blah", function (str) {
  var val = getvaluefromstr(str);
  // do something with val
  Server.start(serverCallback);
});
function serverCallback(request) {
  // do something
});

调用Server.start()后,它将使str超出范围,最终将被收集。此外,它将使您的缩进更易于管理,这对于更复杂的程序是不容小觑的。

至于val,你可以在这种情况下使它成为一个全局变量,这将大大简化你的代码。当然你没必要,你可以和闭包搏斗,但在这种情况下,使val全局或使它存在于readfile回调和serverCallback函数共同的外部作用域中似乎是最直接的溶液

请记住,在您可以使用匿名函数的任何地方,您也可以使用命名函数,以及您可以选择在哪个范围内生活的函数。

答案 2 :(得分:1)

我的猜测是str不会被垃圾收集,因为它可以被restofprogram()使用。 是的,如果在外面宣布restofprogram,str应该得到GCed,除非你做这样的事情:

function restofprogram(val) { ... }

readfile("blah", function(str) {
  var val = getvaluefromstr(str);
  restofprogram(val, str);
});

或者,如果将getvaluefromstr声明为:

function getvaluefromstr(str) {
  return {
    orig: str, 
    some_funky_stuff: 23
  };
}

后续问题:v8是做普通GC还是做GC和ref的组合。计数(比如python?)