【问题标题】:NodeJS Readable stream causing memory leakNodeJS可读流导致内存泄漏
【发布时间】:2021-02-08 22:11:42
【问题描述】:

我在一个项目中遇到了内存泄漏,我已经设法在一个简单的例子中重现了这个问题:

const { Readable } = require("stream");

for (let i=0; i<10000000; i++) {
    const r = new Readable();
    r.push(Buffer.from("a"));

    if (i % 10000 === 0) {
        const memory = process.memoryUsage();
        console.log(memory.heapUsed, memory.heapTotal);
    }
}

使用node --max-old-space-size=1024 test.js 运行,这样它就不会吃掉你所有的内存并始终如一地演示崩溃。

每次迭代都会泄漏内存,但我不知道为什么。销毁流似乎没有任何作用。我没有存储对数据的任何引用,所以 GC 应该在每个循环之后处理并清理它,但不是吗?

节点 12.18.3


更新: This 是我实际项目中存在问题的区域。我的项目是一个用于 RTS 游戏重播文件的解析器,旨在从重播中提取有意义的数据,然后将这些数据存储在数据库中。这不是时间关键,但我希望它相当快,所以它不会得到备份。有 1 或 2 次问题重播,这些游戏更长,包含更多数据,那时我注意到我的解析器有这个内存问题。

使用expose-gc 标志并调用global.gc() 似乎无济于事,但使其异步并时不时等待约10ms 的小延迟完全解决问题而不会耽搁太多,但是这感觉像是一个糟糕的解决方案。

const { Readable } = require("stream");

(async () => {
    for (let i=0; i<10000000; i++) {
        const r = new Readable();
        r.push(Buffer.from("a"));
        r.push(null);
    
        if (i % 10000 === 0) {
            const memory = process.memoryUsage();
            console.log(memory.heapUsed, memory.heapTotal);

            // global.gc(); // no luck

            await delay(10);
        }
    }
})();

function delay(ms) {
    return new Promise(resolve => {
        setTimeout(() => resolve(), ms);
    });
}

【问题讨论】:

  • GC 通常不会运行,直到你的 JS 回到事件循环并且有一些空闲周期让 GC 可以完成它的工作。如果您处于紧密循环中,创建数百万个对象,那么在循环完成之前这些对象不会被 GC。如果您在循环中创建太多,kaboom 就会耗尽内存。每次循环迭代都没有 GC。事物符合 GC 条件,但在解释器有时间运行 GC 进程之前不会真正被释放。仅供参考,这不是泄漏。这是由您编写代码的方式引起的高峰值内存使用量。
  • @jfriend00 那有没有办法手动调用GC或者给节点喘息的空间来自动运行呢?
  • 有。它需要一个命令行参数。见How to request the garbage collector in nodejs to run。通常不建议手动运行 GC,因为通常有更好的方法来设计代码以降低峰值内存使用量,因此不需要它。您尚未显示您的真实代码,因此我们无法对此提出建议。我还建议您阅读 this article 以了解有关 GC 的更多信息。
  • 谢谢,我会做一些阅读和玩那个标志。简而言之,我的项目正在读取一个二进制文件并将其拆分为多个块,我在 while 循环中使用可读流处理每个块,直到它们都被读取并提取出有意义的数据。导致问题的实际部分与我的问题中的示例代码基本完全相同
  • 例如,只取一个大字符串并在其上调用.split() 至少会使该数据使用的内存增加一倍甚至三倍,因为您不仅要为每条数据制作一个新副本在.split() 结果中,但它创建的每个新对象都有一些开销。它都不会永久泄漏(因为它都会被 GCed),它只是更高的峰值内存使用量。然后,开始解析.split() 数组中的每个结果,您可以非常轻松地再次使用两倍或三倍。

标签: node.js memory


【解决方案1】:

我猜您的问题与此错误/行为有关:https://github.com/nodejs/node/issues/38300

已在节点v16.1.0v14.17.1中修复

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-04-19
    • 1970-01-01
    • 1970-01-01
    • 2015-07-06
    • 2014-06-07
    • 2013-11-20
    • 2011-10-28
    相关资源
    最近更新 更多