【问题标题】:nodejs , socket.io simple code memory leaknodejs , socket.io 简单代码内存泄漏
【发布时间】:2015-06-10 20:32:43
【问题描述】:

我使用 nodejs 和 socket.io 为一个简单的套接字应用程序编写了下面的代码(只需连接和断开连接),大约 50 个用户的内存使用量变化不大,但对于多达 300 个用户,一小时后,内存使用量只是长大了(server.js进程接近300MB,随着时间的推移而增长),看起来nodejs不释放内存。

var server = require('http').createServer();
var io = require('socket.io')(server);
var port = 9090;
var sockets = {};

server.listen(port, function () {
    console.log('Server listening at port ', port);
    //timer and logs are not problem , i tested it before.
    setInterval(function(){
      console.log(Object.keys(sockets).length+' Online Devices At '+Date());
    }, 1000 * 60 * 1); 
});

io.on('connection',function(socket){
    sockets[socket.id]={id:socket.id};
    console.log('connected '+socket.id + ' count ' + Object.keys(sockets).length);
    socket.on('disconnect', function (data) {
        delete sockets[socket.id];
        console.log('disconnected '+socket.id+ ' count ' +Object.keys(sockets).length);
    });
});

我是不是做错了什么?!

编辑

使用forever 启动文件后 14 小时

300 个打开的套接字和大约 500MB 的内存使用量与我的 nodejs 进程有关。

编辑

16 小时后,300 个连接的套接字 进程停止后。

ٍ编辑

请查看我的新代码。

var server = require('http').createServer();
var io = require('socket.io')(server);
var port = 90;
var counter = 0;
var clients = {}
server.listen(port, function () {
        console.log('Server listening at port ', port);
});
io.on("connection",function(socket){
        clients[socket.id] = socket;
        counter++;

        socket.on('disconnect', function (data) {
                counter--;
                delete clients[socket.id];
        });
});

我正在与 1000 个连接的用户一起尝试此操作(另一台服务器正在模拟用户请求并打开套接字)

开始前的内存使用量:100MB,5 分钟后和 1000 个稳定打开的连接:400MB

【问题讨论】:

    标签: node.js memory-leaks socket.io


    【解决方案1】:

    V8 在释放未使用的内存方面是懒惰的,因此当 V8 实际上只是没有运行其垃圾收集器时,它可能看起来像内存泄漏。要查看是否是这种情况,请在设置 --expose-gc 标志的情况下运行您的进程,例如

    node --expose-gc yourscript.js
    

    并在一定间隔内强制进行手动垃圾回收(我使用了 30 秒间隔)。

    setInterval(function(){
      global.gc();
      console.log('GC done')
    }, 1000*30);
    

    【讨论】:

    • 谢谢,但请检查我的新代码! ,即使在断开 1000 个套接字并且数组大小为 0 且没有任何日志之后,5 分钟的 300MB 也太多了:|
    • 如果从代码中完全删除clients 对象会怎样?您是否仍然遇到泄漏?
    • 同样的结果! ,我感觉socket.io有问题
    • 只是为了跟进这个问题,因为我遇到了一个类似的问题,我的内存占用量会因未知原因不断增长。据说 V8 在释放内存方面非常懒惰,并且会尽可能多地占用内存。我最终手动调用了垃圾收集器,这使我的内存使用量在很长一段时间内从 500 下降到不到 130。我修改了答案。
    • --expose-gc 还是--enable-gc?您的第一个语句和您的第一个代码 sn-p 中有一些相互矛盾的建议。
    【解决方案2】:

    我在 socket.io 上也遇到过类似的问题

    所以我要解决这个问题是:

    io.on('connection', function (socket) {
        socket.on('disconnect', function (data) {
            destroy();
        });
    
        var alive = Date.now();
        socket.on('am_alive', (data) => {
            alive = Date.now();
        }); // client tell the server that it is alive
    
        const intv = setInterval(() => {
            if (Date.now() > alive + 20000) {
                //sever checks if clients has no activity in last 20s
                destroy();
                clearInterval(intv);
            }
        }, 10000);
    
        function destroy() {
            try {
                socket.disconnect();
                socket.removeAllListeners();
                socket = null; //this will kill all event listeners working with socket
                //set some other stuffs to NULL
            } catch {}
        }
    });
    

    如果您正在考虑为什么我要手动检查服务器是否断开连接,而不是依赖socket.on('disconnect')。 原因是,Socket.io 客户端必须在断开连接之前通知服务器它会断开自己的连接。但是如果客户端的网络出现故障或者 Socket.io 客户端运行在浏览器之外的某个平台上,比如 JAVA、Dart 等,除非您编写 APP 手动通知服务器断开连接,否则 Socket.io_Server 永远不会发现它的客户端已断开连接。不出所料,socket.state 也会返回“已连接”。

    因此,要克服该服务器必须手动查找其客户端。

    【讨论】:

      【解决方案3】:

      代码看起来不错。您提出的内存泄漏几乎肯定不在您共享的代码部分。

      这与您的主要问题无关,但如果您只想列出已连接套接字的数量,您应该使用整数计数器而不是在 sockets 对象上调用 Object.keys(),如下所示:

      var express = require('express');
      var app     = express();
      var server  = require('http').createServer(app);
      var io      = require('socket.io')(server);
      
      var port = 9090;
      var connectedSockets = 0;
      var sockets = {};
      
      server.listen(port, function () {
          console.log('Server listening at port ', port);
          //timer and logs are not problem , i tested it before.
          setInterval(function(){
            console.log(connectedSockets + ' Online Devices At ' + Date());
          }, 1000 * 60 * 1); 
      });
      
      io.on('connection',function(socket){
          if (!sockets[socket.id]) connectedSockets++;
          sockets[socket.id]={ id: socket.id };
          console.log('connected ' + socket.id + ' count ' + connectedSockets);
          socket.on('disconnect', function (data) {
              delete sockets[socket.id];
              connectedSockets--;
              console.log('disconnected ' + socket.id + ' count ' + connectedSockets );
          });
      });
      

      【讨论】:

      • 当sockets对象已经有一个连接的sockets列表时,如何手动维护一个计数器?这与提出的问题有什么关系?
      • @galactocalypse,首先感谢您的回答,但Your proposed memory leak is almost certainly not in the part of the code you have shared. 这是我所有的代码! . about counting sockets我知道! ,但我试图表明问题不在于取消设置变量或数组项。
      • @jfriend00:我在开头提到它This isn't pertinent to your main question。 Object.keys() 在 O(n) 中运行,虽然 300 并不多,但 O(1) 在更大的规模上肯定会更可取。 @ALU0075:我建议您使用 heapdump 并分享您发现的建议:http://jpallen.net/2013/03/08/tracking-down-a-memory-leak-in-node-js-and-socket-io/ 同时分享您正在使用的 node 和 socketio 版本。
      猜你喜欢
      • 1970-01-01
      • 2012-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-22
      • 2015-01-24
      • 1970-01-01
      相关资源
      最近更新 更多