节点没有正确地gc我的对象

时间:2017-10-20 00:04:07

标签: javascript node.js garbage-collection libxml-js

这是一个简单的案例:

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的情况下发生?

2 个答案:

答案 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差异的相关部分

enter image description here

当我们查看4和5 heapdump

时甚至清楚

enter image description here

我们可以从这些堆转换中得出结论:

  • JS部分没有内存泄漏。
  • 根据您的操作系统,heapdump的大小与我们在htop / top / activity监视器上看到的进程大小不匹配。 (12%的heapdump与RAM中的少量Gb相比)

Heapdump只会给我们内存泄漏which are in JS。由于此库具有c代码,因此heapdump不会捕获将存在的泄漏。

我不确定如何从该库中捕获转储或者为什么将其设置为null允许释放内存但是应该可以安全地假设节点gc正在尽其所能。

希望这有帮助