我很好奇嵌套函数的 Node.js 模式如何与 v8 的垃圾收集器一起工作。
这是一个简单的例子
readfile("blah", function(str) {
var val = getvaluefromstr(str);
function restofprogram(val2) { ... } (val)
})
,如果 Restofprogram 长时间运行,这是否意味着 str 永远不会被垃圾收集?我的理解是,使用节点最终会得到很多嵌套函数。如果restofprogram是在外部声明的,那么str不能在范围内,这是否会被垃圾收集?这是推荐的做法吗?
编辑我并不想让问题变得复杂。这只是粗心,所以我修改了它。
I was curious about how the node.js pattern of nested functions works with the garbage collector of v8.
here's a simple example
readfile("blah", function(str) {
var val = getvaluefromstr(str);
function restofprogram(val2) { ... } (val)
})
if restofprogram is long-running, doesn't that mean that str will never get garbage collected? My understanding is that with node you end up with nested functions a lot. Does this get garbage collected if restofprogram was declared outside, so str could not be in scope? Is this a recommended practice?
EDIT I didn't intend to make the problem complicated. That was just carelessness, so I've modified it.
发布评论
评论(3)
简单的答案:如果
str
的值没有从其他任何地方引用(并且str
本身没有从restofprogram
引用),它将变得无法访问,因为一旦function (str) { ... }
返回。详细信息:V8 编译器将真正的本地变量与闭包捕获的所谓上下文变量区分开来,由with语句或
遮蔽eval 调用。
局部变量存在于堆栈中,并在函数执行完成后立即消失。
上下文变量存在于堆分配的上下文结构中。当上下文结构消失时它们就会消失。这里需要注意的重要一点是,来自同一作用域的上下文变量位于相同结构中。让我用一个示例代码来说明:
在这个示例中,一旦
outer
返回,变量x
就会消失,但变量y
和z仅当两者
才会消失。发生这种情况是因为inner1
、inner2
和inner3
死亡时y
和z
分配在相同的上下文结构中,并且所有三个闭包隐式引用此上下文结构(甚至不使用的inner3
明确地)。当您开始使用 with 语句、try/catch 语句时,情况会变得更加复杂,在 V8 上,该语句在 catch 中包含隐式 <strong>with 语句子句或全局
eval
。在此示例中,仅当
inner
死亡时,x
才会消失。因为:这会强制
x
成为上下文变量,并且inner
捕获上下文,因此x
存在,直到inner
消亡。一般来说,如果您想确保给定变量保留某些对象的时间不会超过实际需要的时间,您可以通过将
null
分配给该变量来轻松销毁此链接。Simple answer: if value of the
str
is not referenced from anywhere else (andstr
itself is not referenced fromrestofprogram
) it will become unreachable as soon as thefunction (str) { ... }
returns.Details: V8 compiler distinguishes real local variables from so called context variables captured by a closure, shadowed by a with-statement or an
eval
invocation.Local variables live on the stack and disappear as soon as function execution completes.
Context variables live in a heap allocated context structure. They disappear when the context structure dies. Important thing to note here is that context variables from the same scope live in the same structure. Let me illustrate it with an example code:
In this example variable
x
will disappear as soon asouter
returns but variablesy
andz
will disappear only when bothinner1
,inner2
andinner3
die. This happens becausey
andz
are allocated in the same context structure and all three closures implicitly reference this context structure (eveninner3
which does not use it explicitly).Situation gets even more complicated when you start using with-statement, try/catch-statement which on V8 contains an implicit with-statement inside catch clause or global
eval
.In this example
x
will disappear only wheninner
dies. Because:This forces
x
to become a context variable andinner
captures the context sox
exists untilinner
dies.In general if you want to be sure that given variable does not retain some object for longer than really needed you can easily destroy this link by assigning
null
to that variable.实际上你的例子有点棘手。是故意的吗?您似乎使用内部词法范围的 Restofprogram() 的
val
参数来屏蔽外部val
变量,而不是实际使用它。但无论如何,您询问的是str
,所以为了简单起见,让我忽略示例中val
的棘手之处。我的猜测是,在restofprogram()函数完成之前,str变量不会被收集,即使它不使用它。 如果restofprogram()不使用
str
并且它不使用eval()
和new Function()
那么它可以被安全地收集,但我怀疑它会。对于 V8 来说,这将是一个棘手的优化,可能不值得这么麻烦。如果语言中没有eval
和new Function()
那么事情就会容易得多。现在,它并不一定意味着它永远不会被收集,因为单线程事件循环中的任何事件处理程序都应该几乎立即完成。否则你的整个进程将被阻塞,并且你会遇到比内存中一个无用变量更大的问题。
现在我想知道您的意思是否与您在示例中实际编写的内容不同。 Node 中的整个程序就像在浏览器中一样 – 它只是注册事件回调,这些回调在主程序主体完成后异步触发。此外,没有一个处理程序被阻塞,因此没有任何函数实际上需要任何明显的时间来完成。我不确定我是否理解您问题中的实际含义,但我希望我所写的内容将有助于理解这一切是如何运作的。
更新:
在阅读了有关您的程序外观的评论中的更多信息后,我可以说更多。
如果你的程序是这样的:
那么你也可以这样写:
它会让
str
在调用 Server.start() 后超出范围并最终被收集。此外,它将使您的缩进更易于管理,对于更复杂的程序来说,这一点不可低估。至于
val
在这种情况下,您可以将其设为全局变量,这将大大简化您的代码。当然,您不必这样做,您可以与闭包搏斗,但在这种情况下,使val
全局化或使其位于 readfile 回调和 serverCallback 函数共同的外部作用域中似乎是这样最直接的解决方案。请记住,当您可以使用匿名函数时,您也可以使用命名函数,并且您可以选择希望它们存在于哪个范围内。
Actually your example is somewhat tricky. Was it on purpose? You seem to be masking the outer
val
variable with an inner lexically scoped restofprogram()'sval
argument, instead of actually using it. But anyway, you're asking aboutstr
so let me ignore the trickiness ofval
in your example just for the sake of simplicity.My guess would be that the
str
variable won't get collected before the restofprogram() function finishes, even if it doesn't use it. If the restofprogram() doesn't usestr
and it doesn't useeval()
andnew Function()
then it could be safely collected but I doubt it would. This would be a tricky optimization for V8 probably not worth the trouble. If there was noeval
andnew Function()
in the language then it would be much easier.Now, it doesn't have to mean that it would never get collected because any event handler in a single-threaded event loop should finish almost instantly. Otherwise your whole process would be blocked and you'd have bigger problems than one useless variable in memory.
Now I wonder if you didn't mean something else than what you actually wrote in your example. The whole program in Node is just like in the browser – it just registers event callbacks that are fired asynchronously later after the main program body has already finished. Also none of the handlers are blocking so no function is actually taking any noticeable time to finish. I'm not sure if I understood what you actually meant in your question but I hope that what I've written will be helpful to understand how it all works.
Update:
After reading more info in the comments on how your program looks like I can say more.
If your program is something like:
Then you can also write it like this:
It will make the
str
go out of scope after Server.start() is called and will eventually get collected. Also, it will make your indentation more manageable which is not to be underestimated for more complex programs.As for the
val
you might make it a global variable in this case which would greatly simplify your code. Of course you don't have to, you can wrestle with closures, but in this case makingval
global or making it live in an outer scope common for both the readfile callback and for the serverCallback function seems like the most straightforward solution.Remember that everywhere when you can use an anonymous function you can also use a named function, and with those you can choose in which scope do you want them to live.
我的猜测是 str 不会被垃圾回收,因为它可以被 restofprogram() 使用。
是的,如果restofprogram在外部声明,str应该被GCed,除非你做了这样的事情:
或者如果getvaluefromstr被声明为这样的东西:
后续问题:v8只是做普通的GC还是它做GC 和 ref 的组合。计数(比如Python?)
My guess is that str will NOT be garbage collected because it can be used by restofprogram().
Yes, and str should get GCed if restofprogram was declared outside, except, if you do something like this:
Or if getvaluefromstr is declared as something like this:
Follow-up-question: Does v8 do just plain'ol GC or does it do a combination of GC and ref. counting (like python?)