node.js的垃圾收集

Vis*_*hnu 29 javascript v8 node.js

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

readfile("blah", function(str) {
   var val = getvaluefromstr(str);
   function restofprogram(val2) { ... } (val)
})
Run Code Online (Sandbox Code Playgroud)

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

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

Vya*_*rov 68

简单的答案:如果str未从其他任何地方引用的值(并且str本身未被引用restofprogram),则一旦function (str) { ... }返回就会变得无法访问.

详细信息:V8编译器将实际局部变量与由闭包捕获的所谓上下文变量区分开来,由with -statement或evalinvocation 遮蔽.

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

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

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];
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中变量x将尽快消失outer的回报,但变量y,并z只有当将消失两个 inner1,inner2 inner3死.发生这种情况是因为y并且z在相同的上下文结构中分配,并且所有三个闭包都隐式引用了这个上下文结构(即使inner3它没有明确地使用它).

当你开始使用情况变得更加复杂语句来,的try/catch语句来其在V8中包含一个隐含的语句来里面的catch子句或全局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;
}
Run Code Online (Sandbox Code Playgroud)

在这个例子中,x只有在inner死亡时才会消失.因为:

  • try/catch -contains 隐式使用 catch子句中的-statement
  • V8假设任何with -statement都会影响所有本地人

这迫使x成为一个上下文变量并inner捕获上下文,x直到inner死亡为止.

一般来说,如果你想确定给定的变量不会保留某个对象超过实际需要的时间,你可以通过分配给该变量来轻松破坏这个链接null.


rsp*_*rsp 5

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

\n\n

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

\n\n

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

\n\n

现在我想知道您的意思是否与您在示例中实际编写的内容不同。Node 中的整个程序就像浏览器中的 \xe2\x80\x93 一样,它只是注册事件回调,这些回调在主程序主体完成后异步触发。此外,没有一个处理程序被阻塞,因此没有任何函数实际上需要任何明显的时间来完成。我不确定我是否理解您问题中的实际含义,但我希望我所写的内容将有助于理解这一切是如何运作的。

\n\n

更新:

\n\n

在阅读了有关您的程序外观的评论中的更多信息后,我可以说更多。

\n\n

如果你的程序是这样的:

\n\n
readfile("blah", function (str) {\n  var val = getvaluefromstr(str);\n  // do something with val\n  Server.start(function (request) {\n    // do something\n  });\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

那么你也可以这样写:

\n\n
readfile("blah", function (str) {\n  var val = getvaluefromstr(str);\n  // do something with val\n  Server.start(serverCallback);\n});\nfunction serverCallback(request) {\n  // do something\n});\n
Run Code Online (Sandbox Code Playgroud)\n\n

str调用 Server.start() 后,它将超出范围并最终被收集。此外,它将使您的缩进更易于管理,对于更复杂的程序来说,这一点不可低估。

\n\n

至于val在这种情况下,您可以将其设置为全局变量,这将大大简化您的代码。当然,您不必这样做,您可以与闭包搏斗,但在这种情况下,使其成为val全局的或使其位于 readfile 回调和 serverCallback 函数共同的外部作用域中似乎是最直接的解决方案。

\n\n

请记住,当您可以使用匿名函数时,您也可以使用命名函数,并且您可以选择希望它们存在于哪个范围内。

\n