【问题标题】:Node.js net socket memory leak?Node.js 网络套接字内存泄漏?
【发布时间】:2014-10-01 04:30:18
【问题描述】:

我有this script

var net = require("net")
,   heapdump = require("heapdump");
 
function onConnection (client) {
  client.on("data", function (data) {
    client.end();
  });
};
 
function onListen () {
  var socket = net.connect({host: "127.0.0.1", port: 3000});
 
  socket.bigBuffer = new Buffer(50000000); //50MB Buffer
 
  heapdump.writeSnapshot(__dirname + "/initial.heapsnapshot", function writeInitialSnapshot () {
    console.log("Wrote initial heap snapshot");
    socket.write("data");
  });
 
  socket.on("close", function () {
    writeEndSnapshot();
  });
};
 
function writeEndSnapshot () {
  setTimeout(function () {
    console.log("Running GC");
    gc();
 
    heapdump.writeSnapshot(__dirname + "/final.heapsnapshot", function writeFinalSnapshot () {
      console.log("Wrote final heap snapshot");
    });
  }, 1000);
};
 
net.createServer(onConnection).listen(3000, onListen);

test.js 的作用非常简单:

它创建一个net服务器,在收到任何数据后立即关闭客户端,并将其绑定到localhost中的端口3000。当服务器监听时,onListen 函数被执行,它实例化一个连接到服务器的socket,然后进行堆快照(请注意,在 13 行中我们设置了一个 50MB bigBuffer 到套接字,这只是为了在堆快照中轻松定位)

在拍摄初始快照后,sockets 将一些数据写入服务器并结束,这是writeEndSnapshot 函数被执行的时候,它等待下一个滴答声,强制进行 GC(垃圾收集)循环,然后拍摄最终的堆快照。

对我来说似乎很清楚,在拍摄最终快照时,套接字应该已经被释放,因此应该被垃圾收集。但是,当我加载快照时,这不是我在 Google Chrome 的分析器中看到的。

初始堆快照

最终堆快照

正如您在最终快照中所见,bigBuffer 仍由socket 持有。我不知道为什么socket 还活着。在socket 之后,我在保留树中看到的只有owner in TCP @27447。您认为这可能是节点的错误吗?

我非常感谢任何帮助以了解正在发生的事情。

谢谢!

【问题讨论】:

  • 难道底层操作系统还在完成网络(发送数据、等待来自服务器的 TCP ACK、关闭套接字等),因此与套接字相关的一切都是当您假设它是内部的 GC 时,它还没有完全可用。请记住,网络不是操作系统级别的同步任务。这需要时间。我建议您在再次致电gc() 几秒钟后拍摄您的最终快照。
  • @jfriend00 这就是为什么我在等待套接字的“结束”事件来触发 GC。
  • 你确定“结束”事件意味着一切都在套接字上完成吗?另外,你在哪里关闭套接字?
  • @jfriend00 我在拍摄最后一张快照之前设置了一个较大的超时时间(10 秒),我仍然可以在堆中看到socket
  • 在拍摄最后一个快照之前你在哪里关闭套接字?

标签: javascript node.js memory-leaks garbage-collection v8


【解决方案1】:

事实证明,如果只有一个客户端套接字,这将不会被垃圾收集。这是一个带有两个套接字的更新脚本:

var net = require("net")
,   heapdump = require("heapdump");

function onConnection (client) {
  client.on("data", function (data) {
    client.end();
  });
};

function onListen () {
  var socket
  ,   sockets = []
  ,   closedSockets = 0;

  for (var i = 0; i < 2; i++) {
    socket = net.connect({host: "127.0.0.1", port: 3000});
    sockets.push(socket);
    socket.bigBuffer = new Buffer(25000000); //25MB Buffer

    socket.on("close", function () {
      if (++closedSockets == sockets.length) {
        writeEndSnapshot();
      };
    });
  };

  heapdump.writeSnapshot(__dirname + "/initial.heapsnapshot", function () {
    console.log("Wrote initial heap snapshot");
    for (var i = 0; i < sockets.length; i++) {
      sockets[i].write("data");
    };
  });
};

function writeEndSnapshot () {
  setTimeout(function () {
    console.log("Running GC");
    gc();

    heapdump.writeSnapshot(__dirname + "/final.heapsnapshot", function () {
      console.log("Wrote final heap snapshot");
    });
  }, 1000);
};

net.createServer(onConnection).listen(3000, onListen);

现在一切正常。但是,如果您将循环更改为仅实例化一个套接字,您会看到它在堆中保持活动状态。

【讨论】:

    猜你喜欢
    • 2015-08-26
    • 1970-01-01
    • 2012-03-11
    • 2016-01-01
    • 2023-03-29
    • 2011-01-07
    • 2015-05-30
    相关资源
    最近更新 更多