垃圾收集与node.js

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

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

如果restofprogram长时间运行,是不是意味着str永远不会收集垃圾? 我的理解是,对于节点,最终会嵌套function。 如果restofprogram在外部声明,这是否会收集垃圾,所以str不能在范围内。 这是一个推荐的做法?

编辑我没有打算使问题变得复杂。 那只是粗心,所以我修改了它。

简单的回答:如果str值没有从其他地方引用(而str本身没有从restofprogram引用),只要function (str) { ... }返回,它将变得无法访问。

详细信息:V8编译器将真正的本地variables与由闭包捕获的所谓上下文variables区分开来,由带有语句或eval调用进行映射。

局部variables位于堆栈上,并在函数执行完成后立即消失。

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

 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]; } 

在这个例子中,只要outer返回,variablesx就会消失,但只有当inner1inner2 inner3死亡时,variablesyz才会消失。 发生这种情况是因为yz被分配在相同的上下文结构中,并且所有三个闭包隐式地引用这个上下文结构(甚至不明确使用它的inner3 )。

在开始使用-statement, try / catch -statement时,情况会变得更加复杂,在V8中在catch子句或全局eval内部包含一个隐含 -statement。

 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只会在inner死亡时消失。 因为:

  • 在catch子句中尝试/ catch -contains隐式 -statement
  • V8假定任何带有 –语句的阴影都是当地人

这迫使x成为上下文variables, inner捕获上下文,所以x存在,直到inner死亡。

一般情况下,如果你想确保给定的variables不会保留一些比真正需要的对象更长的时间,那么可以通过给这个variables赋值null来容易地销毁这个链接。

其实你的例子有点棘手。 是故意的吗? 你似乎用一个内部词法作用域的restofprogram()的val参数掩盖了外部valvariables,而不是实际使用它。 但无论如何,你是在问str所以让我忽略val中的诡计,只是为了简单起见。

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

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

现在我想知道,如果你的意思不是你实际上写的, Node中的整个程序就像在浏览器中 – 它只是注册了事件callback,在主程序体已经完成之后,事件callback会被asynchronous触发。 也没有任何处理程序阻塞,所以没有任何function实际上需要任何明显的时间来完成。 我不确定我是否理解了你的问题,但是我希望我写的内容能够帮助你理解它是如何工作的。

更新:

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

如果你的程序是这样的:

 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会超出范围,最终会被收集。 此外,它会使您的缩进更易于pipe理,对于更复杂的程序而言,这是不可低估的。

至于val你可能会在这种情况下使它成为一个全局variables,这将大大简化您的代码。 当然,你不需要,你可以用闭包来摔跤,但是在这种情况下,使val成为全局的,或者使它成为一个通用于readfilecallback和serverCallback函数的外部范围,似乎是最直接的解决scheme。

请记住,在任何地方,当你可以使用一个匿名函数时,你也可以使用一个命名函数,并且你可以select在那个范围内你想让它们活着。

我的猜测是str不会被垃圾回收,因为restofprogram()可以使用它。 是的,如果restofprogram是在外面声明的话,那么str应该得到GC,除非你做了这样的事情:

 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?)