【发布时间】:2012-02-07 14:35:24
【问题描述】:
在尝试调试 NodeJS 中的内存泄漏时,我发现它非常困难(鉴于缺乏我所知道的分析工具)。
我想我会回到基础,并确保我了解如何在 NodeJS 中专门创建内存泄漏。我对可能导致内存泄漏的闭包类型感到困惑,并且不确定垃圾收集器需要什么来释放该内存。
您能否举一些会导致 Node.js 内存泄漏的基本模式示例?
【问题讨论】:
标签: javascript node.js
在尝试调试 NodeJS 中的内存泄漏时,我发现它非常困难(鉴于缺乏我所知道的分析工具)。
我想我会回到基础,并确保我了解如何在 NodeJS 中专门创建内存泄漏。我对可能导致内存泄漏的闭包类型感到困惑,并且不确定垃圾收集器需要什么来释放该内存。
您能否举一些会导致 Node.js 内存泄漏的基本模式示例?
【问题讨论】:
标签: javascript node.js
确切地说不是“泄漏”,但这可能是一个常见的陷阱。
var fn = (function() {
var a = "super long string ...";
var b = "useless value";
var c = "Hello, World!";
return function() {
return c;
};
})();
这将返回一个引用范围的函数,并且该范围内的每个本地变量都将被保留,即使只需要其中一个值。这会导致内存使用量超出您的需要,特别是如果您的函数使用一个小变量,但该范围内有您不需要继续引用的大值。
如何解决?
简单的选择是在函数结束时将你不关心的变量清空。变量仍在范围内,但它们的数据将被释放。
var fn = (function() {
var a = "super long string ...";
var b = "useless value";
var c = "Hello, World!";
// do stuff with a and b
a = b = null;
return function() {
return c;
};
})();
或者你可以将任何使用临时变量的东西分解成它自己的函数,这样它们的作用域就可以被释放。对于大型项目,这是一个更好的解决方案。
var doSetup = function() {
var a = "super long string ...";
var b = "useless value";
// do stuff with a and b
};
var fn = (function() {
doSetup();
var c = "Hello, World!";
return function() {
return c;
};
})();
【讨论】:
您可以使用 node-inspector 通过 Chrome 开发工具调试节点应用程序。
关于未使用的闭包变量的公认答案是错误的,因为在现代 JS 引擎中,只有那些在内部函数中实际引用的变量才包含在闭包范围内。其余的会自动进行垃圾收集。换句话说,这个理论上的条件永远不会在 Node 中真正发生。
对于使用 express 的真实(且相当常见)示例,您可以创建中间件,为每个请求将文件加载到内存中,然后在同一请求中抛出未处理的异常,捕获抛出的异常,然后失败退出进程。
抛出的异常会导致加载的请求资源在请求/响应周期结束时滞留而不是被清理。
当异常发生时无法退出进程意味着Node不会关闭并被PM2或Forever之类的东西重新启动,而是会忽略错误并继续处理新请求,就像什么都没发生一样。由于资源没有被清理,进程会随着时间的推移消耗越来越多的内存,直到它拖累机器的性能并最终耗尽空间来分配新的资源。
这显然会对用户体验产生负面影响。
【讨论】:
发件人:http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml?showone=Closures#Closures
但是要记住的一点是,闭包会保留指向其封闭范围的指针。因此,将闭包附加到 DOM 元素会创建循环引用,从而导致内存泄漏。例如,在以下代码中:
function foo(element, a, b) {
element.onclick = function() { /* uses a and b */ };
}
函数闭包保持对元素、a 和 b 的引用,即使它从不使用元素。由于 element 还保留对闭包的引用,因此我们有一个不会被垃圾收集清理的循环。在这些情况下,代码的结构可以如下:
function foo(element, a, b) {
element.onclick = bar(a, b);
}
function bar(a, b) {
return function() { /* uses a and b */ }
}
【讨论】: