我是这种javascript的新手,所以我会给出一个简短的解释:
我有一个内置于Nodejs
的网络抓取工具,可以收集(相当多)数据,并使用Cheerio
处理它(基本上jQuery
为Node
)创建一个对象然后将其上传到mongoDB。
它工作得很好,除了在较大的网站上。 出现的内容是:
request
模块获取页面源,然后将数据加载到Cheerio
。 正如我所说,这发生在成千上万的URL上,一旦我到达,比如说,加载了10,000个网址,我就会在节点中出错。最常见的是:
Node: Fatal JS Error: Process out of memory
好的,这是实际的问题:
我认为这种情况正在发生,因为Node的垃圾清理工作不正常。例如,从所有40,000个网址中删除的request
数据仍然可能在内存中,或者至少可以创建40,000个javascript对象。也许这也是因为MongoDB连接是在会话开始时进行的,并且永远不会关闭(我只需在完成所有产品后手动关闭脚本)。这是为了避免每次登录新产品时都打开/关闭连接。
要真正确保它们被正确清理(一旦产品进入MongoDB我不再使用它并且可以从内存中删除)可以/我应该只是简单地从内存中删除它,只需使用{{1} }?
Moreso(我显然不知道JS如何处理对象)如果我删除一个对象的引用是完全从内存中删除,还是我必须删除所有这些?
例如:
delete product
答案 0 :(得分:10)
delete
不用于删除变量或空闲内存。它仅用于从对象中删除属性。您可以在delete
运算符上找到this article一个很好的阅读。
您可以通过将变量设置为null
来删除对变量中保存的数据的引用。如果没有其他对该数据的引用,那么这将使其符合垃圾收集的条件。如果有对该对象的其他引用,那么在没有更多引用之前它将不会从内存中清除(例如,代码无法访问它)。
至于导致内存累积的原因,有很多可能性,我们无法真正看到你的代码,知道可以保留哪些引用可以防止GC释放出来。
如果这是一个没有中断执行的单个长时间运行的进程,您可能还需要手动运行垃圾收集器,以确保它有机会清理已发布的内容。
以下是关于在node.js中跟踪内存使用情况的几篇文章:http://dtrace.org/blogs/bmc/2012/05/05/debugging-node-js-memory-leaks/和https://hacks.mozilla.org/2012/11/tracking-down-memory-leaks-in-node-js-a-node-js-holiday-season/。
答案 1 :(得分:3)
JavaScript有一个垃圾收集器,可以自动跟踪哪个变量“可以访问”。如果变量“可达”,则不会释放其值。
例如,如果你有一个全局变量var g_hugeArray并且你为它分配了一个庞大的数组,那么你实际上有两个JavaScript对象:一个是保存数组数据的巨大块。另一个是窗口对象上的属性,其名称为“g_hugeArray”,指向该数据。所以参考链是:window - > g_hugeArray - >实际的数组。
为了释放实际数组,可以使实际数组“无法访问”。你可以打破任何链接上面的链来实现这一目标。如果将g_hugeArray设置为null,则会断开g_hugeArray与实际数组之间的链接。这使得数组数据无法访问,因此它将在垃圾收集器运行时释放。或者,您可以使用“delete window.g_hugeArray”从窗口对象中删除属性“g_hugeArray”。这打破了窗口和g_hugeArray之间的链接,也使实际数组无法访问。
当你有“关闭”时,情况变得更加复杂。当您具有引用局部变量的本地函数时,将创建闭包。例如:
function a()
{
var x = 10;
var y = 20;
setTimeout(function()
{
alert(x);
}, 100);
}
在这种情况下,即使在函数“a”返回后,仍然可以从匿名超时函数到达局部变量x。如果没有超时功能,则只要函数a返回,则局部变量x和y都将变为不可达。但是匿名函数的存在改变了这一点。根据JavaScript引擎的实现方式,它可以选择保留变量x和y(因为它不知道函数是否需要y,直到函数实际运行,这发生在函数a返回之后)。或者,如果它足够聪明,它只能保持x。想象一下,如果x和y都指向大事,那么这可能是一个问题。因此,关闭非常方便,但有时它更有可能导致内存问题,并且可能使跟踪内存问题变得更加困难。
答案 2 :(得分:1)
我在具有类似功能的应用程序中遇到了同样的问题。我一直在寻找内存泄漏或类似的东西。我的进程消耗的内存大小已达到1.4 GB,并且取决于必须下载的链接数。
我注意到的第一件事是在手动运行垃圾收集器之后,几乎所有内存都被释放了。我下载的每个页面大约1 MB,已经处理并存储在数据库中。
然后我安装heapdump并查看应用程序的快照。有关内存分析的更多信息,请参阅Webstorm Blog。
我的猜测是,当应用程序运行时,GC无法启动。为此,我开始使用标志--expose-gc
运行应用程序,并在程序实施时开始手动运行GC。
const runGCIfNeeded = (() => {
let i = 0;
return function runGCIfNeeded() {
if (i++ > 200) {
i = 0;
if (global.gc) {
global.gc();
} else {
logger.warn('Garbage collection unavailable. Pass --expose-gc when launching node to enable forced garbage collection.');
}
}
};
})();
// run GC check after each iteration
checkProduct(product._id)
.then(/* ... */)
.finally(runGCIfNeeded)
答案 3 :(得分:0)
有趣的是,如果您在全局范围内定义某些内容时未使用const
,let
,var
等,则它似乎是全局对象的属性,并删除返回true。这可能导致其被垃圾回收。我进行了这样的测试,它似乎会对我的内存使用产生预期的影响,请告诉我这是否不正确或您得到的结果是否完全不同:
x = [];
process.memoryUsage();
i = 0;
while(i<1000000) {
x.push(10.5);
}
process.memoryUsage();
delete x
process.memoryUsage();