这是一个简单的案例:
let html = `<<some huge html file>>`
var libxmljs = require("libxmljs");
class MyObject{
constructor(html){
this.doc = libxmljs.parseHtml(html);
this.node = this.doc.root()
}
}
let obj
for(var i = 0; i < 100000; i++){
obj = new MyObject(html)
// if I uncomment the next line it works fine
// obj.node = null
console.log(i)
}
当我运行它时,脚本很快耗尽内存,显然是因为obj.node没有正确收集垃圾。当我认为我已经完成它时,如何确保在没有明确地将其设置为null的情况下发生?
答案 0 :(得分:1)
如果您没有将引用专门存储在类实例中,则对象.root()
似乎更多地返回GC。由于永远不会回收分配的全部堆,因此内存使用量似乎仍然相当大。节点本身似乎使用的内存大约是堆上的两倍,以处理本机libxml代码。也许提出一个issue on libxmljs因为这个嘎嘎叫像一个bug。
不将对象存储在类实例中,但将其传递得更好。
class MyObject{
constructor(){
this.doc = libxmljs.parseHtml(html)
}
get node(){
return this.doc.root()
}
}
使用普通对象也可以更好。
function myObject(){
let doc = libxmljs.parseHtml(html)
let node = doc.root()
return {
doc: doc,
node: node,
}
}
作为替代方案,可以尝试其中一个JS based parsers。
答案 1 :(得分:1)
TL; DR:这是库,而不是节点,这是一个问题。
长答案
这是一个稍微修改过的代码
var heapdump = require('heapdump');
const fs = require('fs');
var libxmljs = require("libxmljs");
const content = fs.readFileSync('./html2.htm');
let id = 0;
class MyObject{
constructor(){
this.doc = libxmljs.parseHtml(content);
this.node = this.doc.root()
}
}
let obj;
function createObject () {
obj = new MyObject(content);
};
try {
for(var i = 0; i < 3000; i++){
createObject();
// if I uncomment the next line it works fine
// obj.node = null
console.log(i);
if (i === 50) {
heapdump.writeSnapshot('/Users/me/3.heapsnapshot');
}
if (i === 100) {
heapdump.writeSnapshot('/Users/me/4.heapsnapshot');
}
if (i === 150) {
heapdump.writeSnapshot('/Users/me/5.heapsnapshot');
}
}
console.log('done');
}
catch(e) {
console.log(e);
}
下面是我们在代码(3和4)中使用的heapdump差异的相关部分
当我们查看4和5 heapdump
时甚至清楚我们可以从这些堆转换中得出结论:
Heapdump只会给我们内存泄漏which are in JS。由于此库具有c代码,因此heapdump不会捕获将存在的泄漏。
我不确定如何从该库中捕获转储或者为什么将其设置为null允许释放内存但是应该可以安全地假设节点gc正在尽其所能。
希望这有帮助