为了账号安全,请及时绑定邮箱和手机立即绑定

使用node.js进行垃圾回收

使用node.js进行垃圾回收

弑天下 2019-10-29 11:08:54
我很好奇嵌套函数的node.js模式如何与v8的垃圾收集器一起工作。这是一个简单的例子readfile("blah", function(str) {   var val = getvaluefromstr(str);   function restofprogram(val2) { ... } (val)})如果restofprogram是长时间运行的,这是否意味着str永远不会收集垃圾?我的理解是,使用结点,您最终会获得很多嵌套函数。如果在外部声明了restofprogram,是否会收集垃圾,因此str不能在范围内?这是推荐做法吗?编辑我不想使问题复杂化。那只是粗心,所以我修改了它。
查看完整描述

3 回答

?
至尊宝的传说

TA贡献1789条经验 获得超10个赞

简单的答案:如果str没有从其他任何地方引用的值(并且str本身也没有从引用restofprogram),则function (str) { ... }返回时将无法访问它的值。


详细说明:V8编译器区分真正的地方,从所谓的变量范围内通过封闭拍摄的变量,由阴影与语句来或eval调用。


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


上下文变量位于分配给堆的上下文结构中。当上下文结构消失时,它们消失。这里要注意的重要一点是,来自同一作用域的上下文变量位于同一结构中。让我用示例代码进行说明:


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

}

在这个例子中变量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;

}

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


try / catch-在catch子句中包含 -statement 隐式

V8假定任何带有 -statement的语句都会遮盖所有本地人

这迫使x成为上下文变量并inner捕获上下文,x直到上下文inner消失为止。


通常,如果您要确保给定的变量保留某个对象的时间不超过实际需要的时间,则可以通过分配给该变量轻松地破坏此链接null。


查看完整回答
反对 回复 2019-10-29
?
智慧大石

TA贡献1946条经验 获得超3个赞

实际上,您的示例有些棘手。是故意的吗?您似乎在用内部词法范围的restofprogram()的参数掩盖了外部val变量val,而不是实际使用它。但是无论如何,您只是在问一个问题,str因此val,为简单起见,让我忽略示例中的棘手问题。


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


现在,它不必表示它永远不会被收集,因为单线程事件循环中的任何事件处理程序都应该立即完成。否则,您的整个过程将被阻塞,并且与内存中一个无用的变量相比,您将面临更大的问题。


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


更新:

在阅读了有关程序外观的注释中的更多信息之后,我可以说更多。


如果您的程序是这样的:


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

});

这将使strServer.start后走出去的范围()被调用,并最终将被收集。而且,这将使您的缩进更易于管理,对于更复杂的程序也不要低估它。


至于val您可能会在这种情况下使其成为全局变量,这将大大简化您的代码。当然,您不必这样做,可以与闭包作斗争,但是在这种情况下,val对于readfile回调和serverCallback函数而言,使global全局化或使其处于外部作用域似乎是最简单的解决方案。


请记住,在任何地方都可以使用匿名函数时,也可以使用命名函数,对于那些匿名函数,您可以选择希望它们在哪个作用域中使用。


查看完整回答
反对 回复 2019-10-29
  • 3 回答
  • 0 关注
  • 821 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信