Javascript 节点不';我不能正确地控制我的目标
这里有一个简单的例子:Javascript 节点不';我不能正确地控制我的目标,javascript,node.js,garbage-collection,libxml-js,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 < 1000
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)
}
let html=``
var libxmljs=require(“libxmljs”);
类MyObject{
构造函数(html){
this.doc=libxmljs.parseHtml(html);
this.node=this.doc.root()
}
}
让obj
对于(变量i=0;i<100000;i++){
obj=新的MyObject(html)
//如果我取消注释下一行,它可以正常工作
//obj.node=null
控制台日志(i)
}
当我运行它时,脚本很快就会耗尽内存,显然是因为obj.node没有正确地进行垃圾收集。当我认为我已经完成时,如何确保在不显式地将其设置为null的情况下发生这种情况?如果不将引用专门存储在类实例中,则对象.root()
返回的GC似乎更多。内存使用情况似乎仍然相当泄漏,因为分配的堆的总量从未回收。节点本身使用的内存似乎是堆上内存的两倍,用于处理本机libxml代码。也许会像臭虫一样发出嘎嘎声
不将对象存储在类实例中,而是通过类实例传递对象效果更好
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,
}
}
作为替代方案,您可以尝试以下方法之一。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);
}
var heapdump=require('heapdump');
常数fs=要求('fs');
var libxmljs=require(“libxmljs”);
const content=fs.readFileSync('./html2.htm');
设id=0;
类MyObject{
构造函数(){
this.doc=libxmljs.parseHtml(内容);
this.node=this.doc.root()
}
}
让obj;
函数createObject(){
obj=新的MyObject(内容);
};
试一试{
对于(变量i=0;i<3000;i++){
createObject();
//如果我取消注释下一行,它可以正常工作
//obj.node=null
控制台日志(i);
如果(i==50){
heapdump.writeSnapshot('/Users/me/3.heapsnapshot');
}
如果(i==100){
heapdump.writeSnapshot('/Users/me/4.heapsnapshot');
}
如果(i==150){
heapdump.writeSnapshot('/Users/me/5.heapsnapshot');
}
}
console.log('done');
}
捕获(e){
控制台日志(e);
}
下面是我们在代码(3和4)中采用的heapdump diff的相关部分
当我们看到4和5重泵时,甚至是清晰的
从这些堆堆中,我们可以得出以下几点结论:
- JS部件中没有内存泄漏李>
- heapdump的大小与我们在htop/top/activity monitor上看到的进程大小不匹配,具体取决于您的操作系统。(12 MB的heapdump而RAM中只有很少的Gb)
希望这能有所帮助但Node gc为什么不使用它呢?我觉得这就像节点中的一个bug。我觉得使用析构函数可以解决我的问题,但是节点团队认为它不需要析构函数,而我的示例表明它可能需要。不过我对Node还不熟悉,所以我希望我忽略了一些显而易见的东西。Node通常会这样做,如果你用普通的JS对象做的话,它会很好。libxmljs基于本机libxml2库,这为更多内存问题提供了可能性。基于JS的解析器可能会慢一些,但不太可能出现这样的问题。目前还不清楚node为什么不能正确地gc'ng我的对象。听起来节点确实需要析构函数,尽管它声称不需要。一个析构函数可以很好地解决我的问题。这个想法是你不需要析构函数在正常的操作虽然。。。就像我说的,
libxmljs
模块在它的原生C数据和JS对象之间保留引用的方式看起来像个bug。嗯,你使用“正常操作”对我来说是个问题。我觉得这是一个正常的操作,应该有一个简单的方法来确保对象被正确地垃圾收集。我想等待其他人对此发表意见,恕我冒犯,谢谢你的回答。谢谢,这是有用的信息。我仍然不确定node是否尽了全力。有趣的是,node--trace\u gc
确实跟踪了JS堆中的一些对象保留。旧空间的增长速度约为操作系统上实际进程内存速度的1/2process.memoryUsage()
会在外部
跟踪内存的顶部显示相同的内容