垃圾收集与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
就会消失,但只有当inner1
, inner2
和 inner3
死亡时,variablesy
和z
才会消失。 发生这种情况是因为y
和z
被分配在相同的上下文结构中,并且所有三个闭包隐式地引用这个上下文结构(甚至不明确使用它的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
参数掩盖了外部val
variables,而不是实际使用它。 但无论如何,你是在问str
所以让我忽略val
中的诡计,只是为了简单起见。
我的猜测是在restofprogram()函数完成之前str
variables不会被收集,即使它不使用它。 如果 restofprogram()不使用str
,并且不使用eval()
和new Function()
那么它可以被安全地收集,但我怀疑它会。 这将是V8的一个棘手的优化可能不值得的麻烦。 如果语言中没有eval
和new 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?)