内存在JavaScript中的泄漏和关闭 - 何时和为什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (10)

你经常在网上读到使用闭包是JavaScript中大量内存泄漏的来源。大多数时候这些文章都提到混合脚本代码和DOM事件,其中脚本指向DOM,反之亦然。

我明白,关闭可能是一个问题。

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

关闭还有什么其他问题?任何人都可以详细说明或者指出我有关于此的一个很好的教程吗?

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

提问于
用户回答回答于

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

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

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

  • Jimb Esser's node-mtrace,它使用GCC mtrace实用程序来分析堆使用情况。
  • Dave Pacheco node-heap-dump拍摄了V8堆的快照,并将整个事件序列化为一个巨大的JSON文件。它包括遍历和调查JavaScript中产生的快照的工具。
  • 丹尼科茨的v8-profilernode-inspector为V8探查,并使用WebKit网络检查节点调试接口提供节点绑定。
  • 菲利克斯·纳斯克的叉子也是这样的,它可以解除保持器的图形
  • FelixGeisendörfer的节点内存泄漏教程是对如何使用v8-profiler和的简短而甜蜜的解释node-debugger,并且目前是大多数Node.js内存泄漏调试的最新技术。
  • Joyent的SmartOS平台,提供一系列工具供您调试Node.js内存泄漏

你可以通过分配null闭包变量来帮助GC解决问题。

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

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

用户回答回答于

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

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

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

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

扫码关注云+社区