内存在JavaScript中的泄漏和closures – 何时和为什么?

你经常在网上看到,使用闭包是JavaScript中一个巨大的内存泄漏源。 大多数情况下,这些文章是指混合脚本代码和DOM事件,脚本指向DOM,反之亦然。

我知道封闭可能是一个问题。

但是Node.js呢? 在这里,我们自然没有DOM – 所以没有机会像浏览器一样产生内存泄漏的副作用。

closures还有什么其他问题? 任何人可以详细说明或指出我一个很好的教程呢?

请注意,这个问题明确地针对Node.js,而不是浏览器。

这个问题提出了类似的问题。 基本上,这个想法是,如果你在callback中使用闭包,你应该在完成时“取消订阅”callback,以便GC知道它不能被再次调用。 这对我有意义。 如果你只是等待被叫,那么GC将很难知道你已经完成了。 通过手动从callback机制中删除闭包,它变成未被引用并可用于收集。

此外,Mozilla发表了一篇关于在Node.js代码中查找内存泄漏的好文章 。 我假设,如果你尝试一些他们的策略,你可以find你的代码的部分expression漏洞的行为。 最佳实践是很好的,但是我认为,理解你的程序的需求,并根据你的经验观察,提出一些个性化的最佳实践更有帮助。

以下是Mozilla文章的一个快速摘录:

  • Jimb Esser的node-mtrace ,它使用GCC mtrace实用程序来分析堆的使用情况。
  • Dave Pacheco的node-heap-dump获取了V8堆的快照,并将整个事件序列化为一个巨大的JSON文件。 它包含遍历和调查JavaScript中产生的快照的工具。
  • Danny Coates的v8-profilernode-inspector器使用WebKit Web Inspector为V8探查器和Nodedebugging接口提供节点绑定。
  • 菲利克斯·纳斯克(Felix Gnass)的叉子也是这样的,它可以禁用保持器图表
  • FelixGeisendörfer的节点内存泄漏教程是如何使用v8-profilernode-debugger的简短而甜蜜的解释,而且目前是大多数Node.js内存泄漏debugging的最新技术。
  • Joyent的SmartOS平台,提供了一个工具库供您debuggingNode.js内存泄漏

这个问题的答案基本上说,你可以通过给闭包variables赋值null来帮助GC出来。

 var closureVar = {}; doWork(function callback() { var data = closureVar.usefulData; // Do a bunch of work closureVar = null; }); 

函数内部声明的任何variables将在函数返回时消失, 除了那些在其他闭包中使用的variables。 在这个例子中, closureVar必须在内存中,直到callback()被调用,但谁知道什么时候会发生? 一旦callback被调用,你可以通过设置你的闭包variables为null来给GC一个提示。

免责声明 :正如您从下面的评论中可以看到的,有一些SO用户说这些信息已经过时,并且对于Node.js来说是无关紧要的。 我还没有确定的答案。 我只是发布我在网上find的东西。

您可以在David Glasser的博客文章中find一个很好的例子和解释。

那么,在这里(我添加了一些评论):

 var theThing = null; var cnt = 0; // helps us to differentiate the leaked objects in the debugger var replaceThing = function() { var originalThing = theThing; var unused = function() { if (originalThing) // originalThing is used in the closure and hence ends up in the lexical environment shared by all closures in that scope console.log("hi"); }; // originalThing = null; // <- nulling originalThing here tells V8 gc to collect it theThing = { longStr: (++cnt) + '_' + (new Array(1000000).join('*')), someMethod: function() { // if not nulled, original thing is now attached to someMethod -> <function scope> -> Closure console.log(someMessage); } }; }; setInterval(replaceThing, 1000); 

请在Chrome开发工具(时间轴选项卡,内存视图,单击logging)中尝试使用或不使用originalThing 。 请注意,上面的例子适用于浏览器和Node.js环境。

也信用,特别是对维亚切斯拉夫Egorov 。

我不同意封闭是造成内存泄漏的原因。 由于垃圾收集较差,这可能适用于旧版本的IE浏览器。 请阅读道格拉斯·克罗克福德(Douglas Crockford)的这篇文章,其中明确指出了什么是内存泄漏

据说没有回收的内存已经泄漏。

泄漏不是问题,高效垃圾收集是。 浏览器和服务器JavaScript应用程序都可能发生泄漏。 以V8为例。 在浏览器中,当切换到不同的窗口/选项时,垃圾收集会在选项卡上发生。 闲置时泄漏被堵塞。 标签可以空闲。

在服务器上事情并不那么容易。 泄漏可能会发生,但是GC不具有成本效益。 服务器经常无法承受GC,否则其性能将受到影响。 当一个节点进程达到一定的内存使用量时,它会踢进GC。 然后泄漏定期被删除。 但泄漏仍然会以更快的速度发生,导致程序崩溃。