【问题标题】:Why does Node.js have incremental memory usage?为什么 Node.js 有增量内存使用?
【发布时间】:2015-06-02 23:10:36
【问题描述】:

我有一个大小超过 100 KB 的 gameserver.js 文件。每次刷新浏览器后,我都会检查我的任务管理器,并且每次刷新都会看到我的 node.exe 内存使用率不断上升。我在这里使用ws 模块:https://github.com/websockets/ws 并认为,你知道吗,my 代码中的某处很可能存在一些内存泄漏......

为了仔细检查和隔离问题,我创建了一个test.js 文件并放入默认的ws 代码块:

var WebSocketServer = require('ws').Server
  , wss = new WebSocketServer({ port: 9300 });

wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
  });
});

然后启动它:

现在,我检查node.exe的内存使用情况:

让我困惑的增量部分是:

如果我刷新与此端口9300 websocket 服务器建立连接的浏览器,然后回头查看我的任务管理器.. 它显示:

现在在:14,500 K

而且它在每次刷新时都会不断上升,所以理论上如果我继续刷新它就会突破屋顶。这是故意的吗? ws 模块中是否存在内存泄漏?我问的全部原因是因为我认为可能在几分钟内或者当用户关闭浏览器时它会重新关闭,但事实并非如此。

以及我想要进行此测试的核心原因是因为我认为我的个人代码中的某个地方存在内存泄漏问题,并且只是想检查它是否不是我,反之亦然。现在我被难住了。

【问题讨论】:

  • 您是否尝试过在服务器端实际关闭这些连接,而不是让它们超时空闲?当它们打开时,它们需要内存来跟踪。如果您的浏览器代码和服务器代码都没有明确关闭它们,则它们必须超时,这可能需要一段时间。即便如此,GC 是被动的而不是激进的,它不会以直观的“立即使用更少的内存更好”的方式运行和释放内存。它不是那样工作的。
  • 哇哦。 Check here 他们传递的参数只是消息和代码,所以我认为我不能专门关闭连接,只需要像你说的那样等待超时。不过我明白你的意思,谢谢。我会尝试找到一种方法来立即关闭它们而不是等待超时。这就是内存使用量增加的原因。
  • @Peter Lyons 我收回这一点,有办法。它是:socket.close();full code,我在“onclose”处理程序上使用它。问题是,即使在这样做之后,内存使用量也不会下降.. 嗯
  • 需要注意的是,v8 中的 GC 有点懒惰,如果不需要,通常不会立即进行垃圾收集。更好的方法是使用自动化来长时间测试套接字的打开和关闭。
  • 我在我的代码中发现了这个问题,但不会发布答案,因为Robert 理论上已经有了最好的答案。但在我的情况下,套接字对象实际上并没有被删除,而是被留下来在以后被垃圾收集。 See Here 对此的官方帖子。那么,我们能从中得到什么?套接字对象DATA在socket.close()之后没有被删除,对象数据仍然存储在内存中,这就是我们看到增加的原因..

标签: node.js websocket


【解决方案1】:

Node.js 应用程序的内存占用增加是完全正常的行为。 Node.js 不断分析你的运行代码,生成优化代码,恢复为未优化代码(如果需要)等。即使对于最简单的应用程序,所有这些都需要相当多的内存(Node.js 本身大部分是编写的在遵循与您自己的代码相同的优化/去优化的 JavaScript 中)。

此外,一个进程可能会在需要时被授予更多内存,但许多操作系统仅在它们决定在其他地方(即另一个进程)需要它时才从进程中删除分配的内存。因此,应用程序可以在高峰期消耗 1 GB 的 RAM,然后启动垃圾收集,使用量下降到 500 MB,但该进程可能仍会保留 1 GB。

检测是否存在内存泄漏

要正确分析内存使用和内存泄漏,必须使用 Node.js 的process.memoryUsage()

您应该设置一个将内存使用量转储到文件中的时间间隔,即每秒一次,然后在几秒钟内对您的应用程序施加一些“压力”(即对于 Web 服务器,发出数千个请求)。然后看看结果,看看内存是一直在增加还是遵循稳定的增加/减少模式。

检测内存泄漏源

最好的工具可能是node-heapdump。您可以将它与 Chrome 调试器一起使用。

  1. 启动您的应用程序并施加初始压力(这是为了生成优化的代码并“预热”您的应用程序)
  2. 当应用空闲时,生成一个堆转储
  3. 执行您怀疑可能会导致内存泄漏的单个附加操作(即多请求) - 这可能是最棘手的部分,尤其是对于大型应用程序
  4. 生成另一个堆转储
  5. 将两个堆转储加载到 Chrome 调试器中并比较它们 - 如果存在内存泄漏,您会看到在单个请求期间分配了一些对象,但之后没有释放
  6. 检查对象以确定泄漏发生的位置

我有机会调查 Sails.js 框架中报告的内存泄漏 - 您可以在 this issue 上查看分析的详细描述(包括漂亮的图表等)。

还有一个 detailed article StrongLoop 的关于使用堆转储的文章 - 我建议看看它。

【讨论】:

  • 哇,信息量很大,会看看提供的那些链接。
【解决方案2】:

垃圾收集器不会一直被调用,因为它会阻塞你的进程。所以 V8 在认为有必要时会启动 GC。

为了查看您是否有内存泄漏,我建议在每次请求后手动启动 GC,以查看您的内存是否仍在增加。通常,如果您没有内存泄漏,则您的内存不应增加。因为 GC 会清理所有未使用的对象。如果在 GC 调用后内存仍在增加,则说明内存泄漏。

要手动启动 GC,您可以这样做,但请注意!不要在生产中使用它;这只是一种清理内存并查看是否存在内存泄漏的方法。

像这样启动 Node.js:

node --expose-gc --always-compact test.js

它会暴露垃圾收集器并迫使它变得激进。调用此方法运行 GC:

global.gc();

在每次点击你的服务器后调用这个方法,看看 GC 是否清理内存。

您还可以在请求之前和之后对您的进程进行两次堆转储以查看差异。

不要在生产或项目中使用它。这只是一种查看是否存在内存泄漏的方法。

【讨论】:

  • 我认为这是可能的最糟糕的建议之一:问题几乎总是不是“积极地”调用 GC,而是代码中的内存泄漏。让 V8 在它认为有必要的时候运行它的 GC,不要碰这个。确保您的代码没有问题,并且不包含任何内存泄漏。使用统计数据收集和分析来找出答案。有很多比“手动启动 GC”更好的建议方式。因此-1ed。
  • 进行诊断而不在生产中使用只是一个想法。我应该准确地说,这不是一个解决方案,而是一个查看您是否有内存泄漏的工具。
  • 谢谢你,Sachar,我认为你的贡献非常有用。
猜你喜欢
  • 2013-02-14
  • 2014-07-11
  • 2021-06-22
  • 1970-01-01
  • 2015-04-07
  • 2017-04-03
  • 2016-06-06
  • 2017-11-20
  • 1970-01-01
相关资源
最近更新 更多