为什么Node.js有增量内存使用?

NiC*_*man 7 websocket node.js

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

因此,要仔细检查并隔离问题,我创建了一个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);
  });
});
Run Code Online (Sandbox Code Playgroud)

并启动它:

在此输入图像描述

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

在此输入图像描述

令我困惑的增量部分是:

如果我刷新我的浏览器连接到这个端口9300websocket服务器,然后回头看我的任务管理器..它显示:

在此输入图像描述

现在是:14,500 K.

并且它在每次刷新时都会不断上升,所以从理论上讲,如果我保持清醒它将会通过屋顶.这是有意的吗?ws可能在某个地方的模块中是否存在内存泄漏?我问的全部原因是因为我认为可能在几分钟内或当用户关闭浏览器时它将返回,但事实并非如此.

我想要做这个测试的核心原因是因为我认为我的个人代码中存在内存泄漏问题并且只是想检查它是不是我,反之亦然.现在我很难过.

Rob*_*ann 21

看到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. 生成另一个heapdump
  5. 将两个heapdump加载到Chrome调试器中并进行比较 - 如果存在内存泄漏,您将看到在该单个请求期间分配了一些对象但之后未释放
  6. 检查对象以确定泄漏发生的位置

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

还有一篇关于使用StrongLoop处理heapdumps 的详细文章 - 我建议看看它.


Sac*_*acr 7

垃圾收集器不会一直调用,因为它会阻止您的进程.因此V8在认为必要时启动GC.

为了查找是否有内存泄漏,我建议在每次请求后手动启动GC,看看你的内存是否仍在上升.通常,如果你没有内存泄漏,你的记忆力不应该增加.因为GC将清除所有未使用的对象.如果GC调用后内存仍在上升,则表明内存泄漏.

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

像这样启动Node.js:

node --expose-gc --always-compact test.js
Run Code Online (Sandbox Code Playgroud)

它将暴露垃圾收集器并迫使它具有攻击性.调用此方法来运行GC:

global.gc();
Run Code Online (Sandbox Code Playgroud)

每次点击服务器后调用此方法,看看GC是否清理内存.

您还可以在请求之前和之后执行两个进程堆转换,以查看差异.

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

  • 我认为这是最糟糕的建议之一:问题几乎总是不是GC被“积极地”调用,而是代码中的内存泄漏。让V8认为有必要运行其GC,请不要触碰它。确保您的代码正常,并且不包含任何内存泄漏。使用统计信息收集和分析来找出。与“手动启动GC”相比,有很多方法是更好的建议。因此-1ed。 (2认同)
  • 只是一个想法,使诊断不用于生产.我应该精确这不是一个解决方案,而是一个工具,看你是否有内存泄漏. (2认同)