node.js JavaScript 中的内存泄漏和闭包 - 何时以及为什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19550253/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Memory leaks and closures in JavaScript - when and why?
提问by Golo Roden
You quite often read on the web that using closures is a massive source of memory leaks in JavaScript. Most of the times these articles refer to mixing script code and DOM events, where the script points to the DOM and vice-versa.
你经常在网上读到使用闭包是 JavaScript 内存泄漏的一个巨大来源。大多数时候,这些文章提到混合脚本代码和 DOM 事件,其中脚本指向 DOM,反之亦然。
I understand that closures can be a problem there.
我知道关闭可能是一个问题。
But what about Node.js? Here, we naturally don't have a DOM - so there is no chance to have memory leaking side effects as in browsers.
但是 Node.js 呢?在这里,我们自然没有 DOM - 所以不会像在浏览器中那样出现内存泄漏的副作用。
What other problems may there be with closures? Can anybody elaborate or point me to a good tutorial on this?
闭包还有哪些其他问题?任何人都可以详细说明或指向我一个很好的教程吗?
Please note that this question explicitly targets Node.js, and not the browser.
请注意,这个问题明确针对 Node.js,而不是浏览器。
采纳答案by RustyTheBoyRobot
This questionasks about something similar. Basically, the idea is that if you use a closure in a callback, you should "unsubscribe" the callback when you are finished so the GC know that it can't be called again. This makes sense to me; if you have a closure just waiting around to be called, the GC will have a hard time knowing that you're finished with it. By manually removing the closure from the callback mechanism, it becomes unreferenced and available for collection.
这个问题询问了类似的事情。基本上,这个想法是,如果您在回调中使用闭包,您应该在完成后“取消订阅”回调,以便 GC 知道它不能再次被调用。这对我来说很有意义;如果你有一个闭包等待被调用,GC 将很难知道你已经完成了它。通过从回调机制中手动删除闭包,它会变为未引用并可用于收集。
Also, Mozilla has published a great article on finding memory leaks in Node.jscode. I would assume that if you try out some of their strategies, you could find parts of your code that express leaky behavior. Best practices are nice and all, but I think it's more helpful to understand your program's needs and come up with some personalized best practices based on what you can empirically observe.
此外,Mozilla 发表了一篇关于在 Node.js代码中查找内存泄漏的精彩文章。我假设如果您尝试他们的一些策略,您可以找到表达泄漏行为的代码部分。最佳实践很好,但我认为了解您的程序需求并根据您的经验观察提出一些个性化的最佳实践更有帮助。
Here's a quick excerpt from the Mozilla article:
这是 Mozilla 文章的快速摘录:
- Jimb Esser's
node-mtrace, which uses the GCCmtraceutility to profile heap usage.- Dave Pacheco's
node-heap-dumptakes a snapshot of the V8 heap and serializes the whole thing out in a huge JSON file. It includes tools to traverse and investigate the resulting snapshot in JavaScript.- Danny Coates's
v8-profilerandnode-inspectorprovide Node bindings for the V8 profiler and a Node debugging interface using the WebKit Web Inspector.- Felix Gnass's fork of the same that un-disables the retainers graph
- Felix Geisend?rfer's Node Memory Leak Tutorial is a short and sweet explanation of how to use the
v8-profilerandnode-debugger, and is presently the state-of-the-art for most Node.js memory leak debugging.- Joyent's SmartOS platform, which furnishes an arsenal of tools at your disposal for debugging Node.js memory leaks
- Jimb Esser 的
node-mtrace,它使用 GCCmtrace实用程序来分析堆使用情况。- Dave Pacheco
node-heap-dump拍摄了 V8 堆的快照,并将整个内容序列化到一个巨大的 JSON 文件中。它包括在 JavaScript 中遍历和调查生成的快照的工具。- Danny Coates
v8-profiler和node-inspector使用 WebKit Web Inspector 为 V8 分析器和节点调试接口提供节点绑定。- Felix Gnass 的 fork 与取消禁用保持器图相同
- Felix Geisend?rfer 的 Node Memory Leak Tutorial 简短而甜蜜地解释了如何使用
v8-profilerandnode-debugger,并且是目前大多数 Node.js 内存泄漏调试的最新技术。- Joyent 的 SmartOS 平台,它提供了一系列工具供您调试 Node.js 内存泄漏
The answers to this questionbasically say that you can help the GC out by assigning nullto closure variables.
这个问题的答案基本上是说你可以通过分配null给闭包变量来帮助 GC 。
var closureVar = {};
doWork(function callback() {
var data = closureVar.usefulData;
// Do a bunch of work
closureVar = null;
});
Any variables declared insidea function will go away when the function returns, exceptthose that are used in other closures. In this example, closureVarhas to be in memory until callback()is called, but who knows when that will happen? Once the callback has been called, you can give a hint to the GC by setting your closure variable to null.
函数内部声明的任何变量在函数返回时都会消失,除了那些在其他闭包中使用的变量。在这个例子中,closureVar必须在内存中直到callback()被调用,但谁知道什么时候会发生呢?调用回调后,您可以通过将闭包变量设置为 null 来向 GC 提供提示。
DISCLAIMER: As you can see from the comments below, there are some SO users who say that this information is out of date and inconsequential for Node.js. I don't have a definitive answer on that yet; I'm just posting what I've found on the web.
免责声明:正如您从下面的评论中看到的那样,有些 SO 用户说此信息已过时且对 Node.js 无关紧要。对此我还没有明确的答案。我只是发布我在网上找到的内容。
回答by borisdiakur
You can find a good example and explanation in this blog postby David Glasser.
您可以在David Glasser 的这篇博文中找到一个很好的示例和解释。
Well, here it is (I added a few comments):
好吧,这是(我添加了一些评论):
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);
Please try it out with and without nulling originalThingin Chrome Dev Tools (timeline tab, memory view, click record). Note that the example above applies to browser and Node.js environments.
请originalThing在 Chrome 开发工具(时间轴选项卡、内存视图、单击记录)中使用或不使用归零进行尝试。请注意,上面的示例适用于浏览器和 Node.js 环境。
Credit also and especially to Vyacheslav Egorov.
还要特别感谢 Vyacheslav Egorov。
回答by user568109
I have to disagree with closures being a cause of memory leaks. This maybe true for older versions of IE because of its shoddy garbage collection. Please read thisarticle from Douglas Crockford, which clearly states what a memory leak is.
我不得不不同意闭包是导致内存泄漏的原因。这对于旧版本的 IE 来说可能是正确的,因为它的垃圾收集很糟糕。请阅读Douglas Crockford 的这篇文章,其中清楚地说明了什么是内存泄漏。
The memory that is not reclaimed is said to have leaked.
没有回收的内存据说已经泄漏了。
Leaks are not the problem, efficient garbage collection is. Leaks can happen in both browser and server JavaScript applications alike. Take V8 as an example. In the browser, garbage collection happens on a tabwhen you switch to different window/tab. The leak is plugged when idle. Tabs can be idle.
泄漏不是问题,有效的垃圾收集才是问题。泄漏可能发生在浏览器和服务器 JavaScript 应用程序中。以V8为例。在浏览器中,当您切换到不同的窗口/选项卡时,会在选项卡上进行垃圾回收。空闲时堵塞泄漏。标签可以空闲。
On the server things are not so easy. Leaks can happen, but GC is not as cost-effective. Servers cannot afford to GC frequently or its performance will be affected. When a node process reaches a certain memory usage, it kicks in the GC. Leaks will then be removed periodically. But leaks still can happen at a faster rate, causing programs to crash.
在服务器上,事情并不那么容易。可能会发生泄漏,但 GC 的成本效益不高。服务器承受不起频繁的GC,否则性能会受到影响。当一个节点进程达到一定的内存使用量时,它会启动 GC。然后将定期清除泄漏。但是泄漏仍然可能以更快的速度发生,从而导致程序崩溃。

