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

Why does Node.js have incremental memory usage?

我有一个 gameserver.js 文件,大小超过 100 KB。每次刷新浏览器后,我都会检查我的任务管理器,并一直看到我的 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 模块中是否存在内存泄漏?我问的全部原因是因为我想也许几分钟后或者当用户关闭浏览器时它会返回,但它不会。

我想做这个测试的核心原因是我认为我的个人代码某处存在内存泄漏问题,只是想检查它是否不是我造成的,反之亦然。现在我被难住了。

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

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

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

像这样启动 Node.js:

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

它会暴露垃圾收集器并迫使它具有侵略性。调用此方法到 运行 GC:

global.gc();

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

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

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

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

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

正在检测是否存在内存泄漏

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

您应该设置一个时间间隔,将此内存使用情况转储到一个文件中,即每秒一次,然后在几秒钟内对您的应用程序应用一些 "stress"(即对于 Web 服务器,发出数千个请求)。然后查看结果,看看内存是否一直在增加,或者是否遵循 increasing/decreasing.

的稳定模式

正在检测内存泄漏源

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

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

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

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