Node.js 带有EventSource的Express.js中存在内存泄漏

Node.js 带有EventSource的Express.js中存在内存泄漏,node.js,express,memory-leaks,server-sent-events,Node.js,Express,Memory Leaks,Server Sent Events,我想我在将x个EventSource客户端连接到Express应用程序时遇到内存泄漏。连接客户端并向其发送x条消息并断开连接后,myExpress应用程序仅释放少量已分配的堆/RSS 为了确认这一点,我在启动服务器时保存了一个Heapdump,在将7000个客户端连接到服务器并向每个客户端发送x条消息后保存了一个。在拍摄堆快照之前,我等待了一段时间,以便让GC有机会进行清理 为了比较这些堆快照,我在ChromeDeveloperTools概要视图中加载了它们,并选择了“比较”模式 我的问题是:

我想我在将x个EventSource客户端连接到Express应用程序时遇到内存泄漏。连接客户端并向其发送x条消息并断开连接后,myExpress应用程序仅释放少量已分配的堆/RSS

为了确认这一点,我在启动服务器时保存了一个Heapdump,在将7000个客户端连接到服务器并向每个客户端发送x条消息后保存了一个。在拍摄堆快照之前,我等待了一段时间,以便让GC有机会进行清理

为了比较这些堆快照,我在ChromeDeveloperTools概要视图中加载了它们,并选择了“比较”模式

我的问题是:

1)如何解释这些数字? (有关参考信息,请参阅随附的堆快照屏幕截图。)

2)例如,Socket对象几乎不能释放任何对象,对吗

3)你能给我更多的建议来调查这个问题吗

至于我的评论

Javascript无法清除任何指向它的内存,大约2年前,有人发现了一个漏洞,它很快就被关闭了,它就是这样工作的

var someData = ["THIS IS SOME DATA SAY IT WAS THE SIZE OF A SMALL APPLICATION"];
var somePointer = someData[0];
delete someData;
然后,他们将一个应用程序注入到somePointer中,因为现在没有数据时,它是对内存位置的引用。嘿,普雷斯托,你注入了记忆

因此,如果有一个像上面这样的引用
somePointer=someData[0]
删除某些数据之前,您无法释放内存,因此您必须删除所有要清理的内容的引用使您的系统内存可以通过所有_客户端访问,所以您可以做的是

第157行

_.each(ALL_CLIENTS, function(client, i) {
                    var u; // holds a undefined value (null, empty, nothing)
                    client.close();
                    //delete ALL_CLIENTS[i];
                    ALL_CLIENTS[i] = u;
                    ALL_CLIENTS.unused++;
                }); 
另一方面,这不是内存泄漏内存泄漏是说你有这个服务器,你关闭它,如果你退出后内存没有释放出来,那么你就有内存泄漏,如果它确实清理了它自身的内存,这不是泄漏,只是内存管理不好

感谢@Magus指出,删除并不是您可以使用的最佳方式,但是我绝不建议您实现限制结构,但您可以尝试

第27行:
ALL_CLIENTS.unused=0

第64行:

var u;
if(ALL_CLIENTS.unused > 0){
    for(var i = 0; i < ALL_CLIENTS.length; i++){
        if(ALL_CLIENTS[i] == u){
            ALL_CLIENTS[i] = this;
            ALL_CLIENTS.unused--;
            i = ALL_CLIENTS.length;
        }
    }
}else{
    ALL_CLIENTS.push(this);
}
var-u;
如果(所有未使用的客户端>0){
对于(变量i=0;i
您可以避免内存泄漏,而且还可以避免垃圾收集器。 你所要做的就是对象轮询

你可以这样做

var clientsPool = new Array(1000);
var clientsConnected = [];
当一个新的客户端连接时,您需要这样做

var newClient = clientsPool.pop();
//set your props here
clientsConnected.push(newClient);
这是一种避免垃圾收集器和防止内存泄漏的好方法。当然,还有一点工作要做,你必须小心处理,但这对性能来说是完全值得的

这是一个很棒的话题,给你

如果有人对我用来测试它的代码感兴趣,请看这里:这不是内存泄漏—您已经添加了对它们的引用,而没有删除它—当您仍然可以访问它时,它不会释放内存
ALL_CLIENT
正在向其添加内容,现在您正在关闭它们,而不是从
ALL_CLIENT
中删除它们,因此在您完成并关闭连接后仍有对它们的引用调用
delete ALL_CLIENT[键]
其中key是当前客户端的key如果你有一个引用,它不能删除其中的数据,否则你可能会有一个对另一个应用程序内存的引用,如果你跳到内存中,这是我发现的第一件事,检查你的代码是否有其他引用,当你想释放内存时,你不会删除这些引用内存增加,请不要使用删除关键字!!它弊大于利。如果以后重用对象,则将其保留在内存中不会有问题。将它们都保存在内存中,并为不同的客户使用相同的对象,这样做可以避免分配时间、释放时间(delete关键字会破坏您的性能)和垃圾收集时间。观看我在答案上发布的视频,了解更多信息,这是值得的。我不是说这是最好的方法,但是它确实表明内存没有泄漏,但是你的大纲不是最好的解决方案,因为跟踪巫婆连接存在和不存在get的负载相当重,你说什么版本的ECMAScript会破坏性能,因为我确信delete关键字只用于告诉清洁器,当清洁器运行时,它可以清理它,但它不会强制运行清洁器…@Magus我已经更新,以显示允许重用且不调用delete的实现,但是我肯定你错了删除我确信它应该提醒清洁器它现在可以清理它,并将值设置为
未定义
,因此降低内存清洁器的负载,因为它不必检查它是否可以被删除,也不必检查它是否被告知可以删除,然后清洁器将锁定任何references@MartinBarker,我可能错了,但我很自信。我在codeschool javascript最佳实践课程中了解了这个关键词。他们的想法是,它会标记要收集的内存,但其糟糕的想法是垃圾收集器无论如何都会运行,如果您只是设置为null,那么使用delete关键字只会让GC更频繁地运行,并影响您的性能。当然,你不会注意到在桌面上,太多的删除和GC事件会破坏你在移动设备上的性能?如果你仔细观察,我们的答案是不同的。当我进行对象轮询时,他遍历所有客户机。此外,他使用了delete关键字,这是非常糟糕的。总之,你