我应该担心清理 Node.js 中的大对象还是留给垃圾收集器?

Should I worry about cleaning up large objects in Node.js or leave it for the garbage collector?

最近我 运行 遇到了 node.js API 的问题,我的内存随着每个请求而变得越来越大。我在 Heroku 上使用他们的免费版本托管我的服务器,该版本只有 512MB 的 RAM。在周末流量很大之后,我开始收到来自 Heroku 的超出内存错误,因此我开始在我的代码中搜索内存泄漏,但无济于事。我没有保留任何物品,所有东西都应该被清理干净,非常 f运行kly,我迷路了。

然而,经过一些研究,我发现 node.js 在达到 max-old-space-size 变量时运行垃圾收集器,并且在 64 位系统上默认为 1024MB。我已将其设置为 410(可用内存的 80%),但想知道我是否应该只在代码中处理它?显然,升级我的实例并仅具有正常的默认上限是最理想的,但现在这不是一个选项。

示例:

// lets assume there is some apiGet function
// that calls back with a very very large object with
// the following structure:
// {
//      status: "success",
//      statusCode: 200,
//      messages: [],
//      data: { users: [ huge array of users ] }
// }
// we have a manipulateData function that's going
// to do some stuff to the returned data and the
// call some callback to give the data to the caller
function manipulateData(callback) {
    apiGet(function(error, veryLargeObject) {
        var users = veryLargeObject.data.users;
        var usefulUsers = users.map(function(user) {
            // do some data calculations here and then
            // return just those properties we needed
        });

        callback(null, usefulUsers)
    });
}

所以在这个例子中,一旦 manipulateData 完成 运行,如果我理解正确,"veyLargeObject" 现在将被设置为垃圾收集,因为没有更多的指针具有访问它(返回的 usefulUsers 是地图创建的新数组)。但这并不一定意味着它占用的所有内存都是空闲的,对吗?在调用回调之前设置 veryLargeObject = null 或 undefined 是否明智?

我希望我问的是有道理的。基本上:当不再打算使用大对象时,将大对象设置为 null 或 undefined 是个好主意,还是应该将它们留给垃圾收集器清理?当您只获得 512MB RAM 与 8GB RAM 时,这个问题的答案是否会改变?

如果您确定不再需要给定对象,则将其设置为 null 是可行的方法(请注意,这并不意味着任何链接对象也将被垃圾收集到)。只有当对该给定对象的所有引用都设置为 null(该对象在您的代码中的任何位置都变得不可访问)时,才会收集它。

由于 node.js 在后台使用 V8 引擎,您可以获得一些关于如何改进垃圾收集的提示 A tour of V8: Garbage Collection. If that's not enough, you can force GC by following these instructions

仅在某些类型的关闭情况下才需要将变量设置为 null,例如:

function createClosure(bigData) {
    var unrelatedVar = 1;

    doSomethingAsync(function theCallback(err, result) {
        if (bigData.matches(result)) {
            ...
        }
    });

    return function theReturnedFunction() {
        return unrelatedVar++;
    };
}

在 V8 中,同级闭包共享闭包变量所在的相同上下文对象。所有同级闭包都指向上下文对象,所以它会一直存活直到所有函数都死掉。所以这里 theReturnedFunctiontheCallback 都是同级函数,都指向同一个上下文对象,有两个成员:bigData 和 unrelatedVar。所以只要 因为返回的函数是活的,所以 bigData 也是活的,即使它不能被引用。

这真的很容易陷入,因为封闭变量看起来就像局部变量,而实际上它们就像一个对象的字段(使用显式 this.field 所以它总是很明显)。这与在未使用后必须将显式对象的 .bigData 字段设置为 null 没有任何不同,但是当它是一个显式对象时,它更难被遗漏。