我发现了一些关于有类似问题的人的参考,其中答案总是,确保完成后调用 window.close() 。然而,这似乎对我不起作用(节点 0.8.14 和 jsdom 0.3.1)
一个简单的重现
var util = require('util');
var jsdom=require('jsdom');
function doOne() {
var htmlDoc = '<html><head></head><body id="' + i + '"></body></html>';
jsdom.env(htmlDoc, null, null, function(errors, window) {
window.close();
});
}
for (var i=1;i< 100000;i++ ) {
doOne();
if(i % 500 == 0) {
console.log(i + ":" + util.inspect(process.memoryUsage()));
}
}
console.log ("done");
我得到的输出是
500:{ rss: 108847104, heapTotal: 115979520, heapUsed: 102696768 }
1000:{ rss: 198250496, heapTotal: 194394624, heapUsed: 190892120 }
1500:{ rss: 267304960, heapTotal: 254246912, heapUsed: 223847712 }
...
11000:{ rss: 1565204480, heapTotal: 1593723904, heapUsed: 1466889432 }
此时风扇开始疯狂运转,测试实际上停止了……或者至少开始变得非常缓慢
除了 window.close 之外,还有谁有其他技巧来消除内存泄漏(或者它看起来确实像内存泄漏)
谢谢!
彼得
使用jsdom 0.6.0帮助抓取一些数据,遇到了同样的问题。
window.close
只能帮助减缓内存泄漏,但它最终确实会蔓延,直到进程被杀死。
运行脚本
node --expose-gc myscript.js
在他们修复内存泄漏之前,除了调用
window.close
之外,手动调用垃圾收集器似乎也有效:
if (process.memoryUsage().heapUsed > 200000000) { // memory use is above 200MB
global.gc();
}
在调用 window.close 后卡住了。每次触发时,内存使用立即回落到基线(对我来说大约 50MB)。几乎察觉不到停顿。
更新:还可以考虑连续多次调用
global.gc()
而不是只调用一次(即global.gc();global.gc();global.gc();global.gc();global.gc();
)
多次调用 window.gc() 更有效(基于我不完善的测试),我怀疑是因为它可能导致 chrome 触发一个主要的 GC 事件而不是一个次要的事件。 - https://github.com/cypress-io/cypress/issues/350#issuecomment-688969443
您没有给程序任何空闲时间来进行垃圾收集。我相信您会遇到同样的问题,任何在循环中紧密创建的大型对象图都不会中断。
CheapSteaks 的答案证实了这一点,它手动强制垃圾收集。如果可行的话,jsdom 中不可能存在内存泄漏,因为根据定义,内存泄漏会阻止垃圾收集器收集泄漏的内存。
我在 jsdom 上遇到了同样的问题,然后切换到 cheerio,它比 jsdom 快得多,甚至在扫描数百个站点后也能工作。也许你也应该尝试一下。唯一的问题是,它没有您可以在 jsdom 中使用的所有选择器。
希望它也适合你。
丹尼尔
with gulp、内存使用、清理、变量删除、window.close()
var gb = setInterval(function () {
//only call if memory use is bove 200MB
if (process.memoryUsage().heapUsed > 200000000) {
global.gc();
}
}, 10000); // 10sec
gulp.task('tester', ['clean:raw2'], function() {
return gulp.src('./raw/*.html')
.pipe(logger())
.pipe(map(function(contents, filename) {
var doc = jsdom.jsdom(contents);
var window = doc.parentWindow;
var $ = jquery(window);
console.log( $('title').text() );
var html = window.document.documentElement.outerHTML;
$( doc ).ready(function() {
console.log( "document loaded" );
window.close();
});
return html;
}))
.pipe(gulp.dest('./raw2'))
.on('end', onEnd);
});
对于 7k 个文件,我的使用量始终在 200mb - 300mb 之间。花了30分钟。 这可能对某人有帮助,因为我用谷歌搜索并没有找到任何有用的东西。
解决此问题的方法是在分叉的 child_process 中运行 jsdom 相关代码,并在完成后发回相关结果。然后杀死child_process。
JSDom 使用
process.nextTick
推迟某些操作。
在同步循环中调用 JSDom 时,事件循环永远不会运行。 因此,对旧 JSDom 窗口对象的引用被保留,节点内存不足。
通过运行一些异步代码,事件循环将被允许运行,并且应该解决内存泄漏问题。
您可以简单地使用零延迟的
setTimeout
来允许事件循环运行。
await new Promise(resolve => setTimeout(resolve, 0));