简体   繁体   English

使用 node.js 进行垃圾收集

[英]garbage collection with node.js

I was curious about how the node.js pattern of nested functions works with the garbage collector of v8.我很好奇嵌套函数的 node.js 模式如何与 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?如果 restofprogram 是长时间运行的,那是不是意味着 str 永远不会被垃圾收集? 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?如果在外部声明了 restofprogram,这是否会被垃圾收集,因此 str 不能在范围内? Is this a recommended practice?这是推荐的做法吗?

EDIT I didn't intend to make the problem complicated.编辑我不打算让问题复杂化。 That was just carelessness, so I've modified it.那只是粗心大意,所以我修改了它。

Simple answer: if value of the str is not referenced from anywhere else (and str itself is not referenced from restofprogram ) it will become unreachable as soon as the function (str) { ... } returns.简单的答案:如果str值没有从其他任何地方引用(并且str本身没有从restofprogram引用),一旦function (str) { ... }返回,它将变得无法访问。

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.详细信息:V8 编译器将真正的局部变量与由闭包捕获的、由with语句或eval调用遮蔽的所谓上下文变量区分开来。

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:让我用一个示例代码来说明它:

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

In this example variable x will disappear as soon as outer returns but variables y and z will disappear only when both inner1 , inner2 and inner3 die.在这个例子中变量x会很快消失作为outer回报,但变量yz会消失仅当两个inner1inner2inner3管芯。 This happens because y and z are allocated in the same context structure and all three closures implicitly reference this context structure (even inner3 which does not use it explicitly).发生这种情况是因为yz被分配在相同的上下文结构中,并且所有三个闭包都隐式地引用了这个上下文结构(甚至没有显式使用它的inner3 )。

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 .当您开始使用with -statement、 try/catch -statement 时,情况会变得更加复杂,它们在 V8 上在 catch 子句或全局eval包含一个隐含的with -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;
}

In this example x will disappear only when inner dies.在这个例子中,只有当inner死亡时x才会消失。 Because:因为:

  • try/catch -contains implicit with -statement in catch clause try / catch语句的catch子句-包含隐语句来
  • V8 assumes that any with -statement shadows all the locals V8 假设 any with -statement 会影响所有locals

This forces x to become a context variable and inner captures the context so x exists until inner dies.这迫使x成为一个上下文变量,而inner捕获上下文,因此x一直存在直到inner消亡。

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.通常,如果您想确保给定的变量不会保留某个对象的时间超过实际需要的时间,您可以通过为该变量分配null来轻松破坏此链接。

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()'s val argument, instead of actually using it.您似乎在用内部词法范围的 restofprogram() 的val参数屏蔽外部val变量,而不是实际使用它。 But anyway, you're asking about str so let me ignore the trickiness of val in your example just for the sake of simplicity.但无论如何,你问的是str所以为了简单起见,让我忽略你的例子中val的技巧。

My guess would be that the str variable won't get collected before the restofprogram() function finishes, even if it doesn't use it.我的猜测是str变量在 restofprogram() 函数完成之前不会被收集,即使它不使用它。 If the restofprogram() doesn't use str and it doesn't use eval() and new Function() then it could be safely collected but I doubt it would.如果restofprogram() 不使用str并且它不使用eval()new Function()那么它可以被安全地收集,但我怀疑它会。 This would be a tricky optimization for V8 probably not worth the trouble.这对于 V8 来说是一个棘手的优化,可能不值得麻烦。 If there was no eval and new Function() in the language then it would be much easier.如果语言中没有evalnew Function()那么它会容易得多。

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. Node 中的整个程序就像在浏览器中一样——它只是注册在主程序主体完成后异步触发的事件回调。 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:如果你的程序是这样的:

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

Then you can also write it like this:那么你也可以这样写:

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

It will make the str go out of scope after Server.start() is called and will eventually get collected.它将在调用 Server.start() 后使str超出范围并最终被收集。 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.至于val ,在这种情况下,您可以将其val全局变量,这将大大简化您的代码。 Of course you don't have to, you can wrestle with closures, but in this case making val 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.当然,您不必这样做,您可以解决闭包问题,但在这种情况下,使val全局化或使其存在于 readfile 回调和 serverCallback 函数通用的外部作用域中似乎是最直接的解决方案。

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.请记住,在任何地方,当您可以使用匿名函数时,您也可以使用命名函数,并且您可以选择希望它们存在于哪个范围内。

My guess is that str will NOT be garbage collected because it can be used by restofprogram().我的猜测是 str 不会被垃圾收集,因为它可以被 restofprogram() 使用。 Yes, and str should get GCed if restofprogram was declared outside, except, if you do something like this:是的,如果在外部声明了 restofprogram,str 应该被 GCed,除非你做这样的事情:

function restofprogram(val) { ... }

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

Or if getvaluefromstr is declared as something like this:或者,如果 getvaluefromstr 被声明为这样的:

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

Follow-up-question: Does v8 do just plain'ol GC or does it do a combination of GC and ref.后续问题:v8 是只执行普通 GC 还是执行 GC 和 ref 的组合。 counting (like python?)计数(像蟒蛇?)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM