您经常在网上看到,使用闭包是JavaScript中的大量内存泄漏源。这些文章大多数时候都是指混合脚本代码和DOM事件,其中脚本指向DOM,反之亦然。
我知道闭包可能是个问题。
但是Node.js怎么样?在这里,我们自然没有DOM - 所以没有机会像浏览器一样有内存泄漏副作用。
关闭可能还有哪些其他问题?任何人都可以详细说明或指出一个很好的教程吗?
请注意,此问题明确针对Node.js,而不是浏览器。
答案 0 :(得分:36)
This question询问类似的事情。基本上,我们的想法是,如果在回调中使用闭包,则应在完成后“取消订阅”回调,以便GC知道无法再次调用它。这对我来说很有意义;如果你有一个关闭只是等待被调用,那么GC将很难知道你已经完成了它。通过从回调机制中手动删除闭包,它将被取消引用并可供收集。
此外,Mozilla已发布a great article on finding memory leaks in Node.js代码。我假设如果你尝试他们的一些策略,你可以找到代表泄漏行为的部分代码。最好的做法很好,但我认为理解你的程序需求更有帮助,并根据你的经验观察得出一些个性化的最佳实践。
以下是Mozilla文章的快速摘录:
- Jimb Esser的
node-mtrace
,它使用GCCmtrace
实用程序来分析堆使用情况。- Dave Pacheco的
node-heap-dump
拍摄了V8堆的快照,并将整个事件序列化为一个巨大的JSON文件。它包括用JavaScript遍历和调查生成的快照的工具。- Danny Coates的
v8-profiler
和node-inspector
使用WebKit Web Inspector为V8探查器和Node调试接口提供节点绑定。- 菲利克斯·格纳斯(Felix Gnass)同样禁用“保留者图”的分叉
- FelixGeisendörfer的节点内存泄漏教程是关于如何使用
v8-profiler
和node-debugger
的简短而精彩的解释,目前是大多数Node.js内存泄漏的最新技术调试。- Joyent的SmartOS平台,为您调试Node.js内存泄漏提供了大量工具
this question的答案基本上说你可以通过将null
分配给闭包变量来帮助GC。
var closureVar = {};
doWork(function callback() {
var data = closureVar.usefulData;
// Do a bunch of work
closureVar = null;
});
在函数返回时,在函数内声明的任何变量都会消失,除了在其他闭包中使用的那些变量。在这个例子中,closureVar
必须在内存中,直到callback()
被调用,但谁知道什么时候会发生?调用回调后,可以通过将闭包变量设置为null来提示GC。
免责声明:正如您在下面的评论中所看到的,有些SO用户表示此信息已过期且对Node.js无关紧要。我还没有明确的答案;我只是发布了我在网上发现的内容。
答案 1 :(得分:10)
David Glasser可以在this blog post中找到一个很好的例子和解释。
嗯,这是(我添加了一些评论):
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开发者工具(时间线标签,内存视图,点击记录)中使用和不使用originalThing
进行验证。请注意,上面的示例适用于浏览器和Node.js环境。
还要归功于Vyacheslav Egorov。
答案 2 :(得分:2)
我不同意闭包是造成内存泄漏的原因。对于旧版本的IE,这可能是正确的,因为它的垃圾收集很少。请阅读Douglas Crockford撰写的this文章,该文章清楚地说明了内存泄漏的原因。
据说没有回收的记忆已经泄露。
泄漏不是问题,高效的垃圾收集是。浏览器和服务器JavaScript应用程序都可能发生泄漏。以V8为例。在浏览器中,当您切换到不同的窗口/选项卡时,垃圾收集会在选项卡上进行。空闲时泄漏堵塞。标签可以闲置。
在服务器上,事情并不那么容易。泄漏可能发生,但GC并不具有成本效益。服务器无法经常使用GC或其性能会受到影响。当节点进程达到某个内存使用量时,它会启动GC。然后将定期清除泄漏。但泄漏仍然可能以更快的速度发生,导致程序崩溃。