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

时间:2015-03-02 20:09:32

标签: javascript node.js garbage-collection v8

最近我遇到了一个node.js API的问题,我的内存随着每个请求而变得越来越大。我正在Heroku上使用他们的免费版本托管我的服务器,该版本只有512MB的RAM。在周末获得大量流量之后,我开始从Heroku开始超出内存错误,所以我开始在我的代码中搜索内存泄漏无济于事。我没有留下任何物品,一切都应该得到清理,坦率地说,我迷路了。

然而,在做了一些研究之后,我发现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”进行垃圾收集,因为没有更多的指针可以访问它(返回的有用的用户是由地图创建的新数组)。但这并不一定意味着它占用的所有内存都是免费的,对吗?在调用回调之前设置veryLargeObject = null或undefined是否明智?

我希望我所要求的是有道理的。基本上:当没有意图再使用它们时,将大对象设置为null或undefined是一个好主意还是应该留下来让垃圾收集器清理?当你只有512MB的RAM而不是8GB的RAM时,这个问题的答案会改变吗?

2 个答案:

答案 0 :(得分:2)

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

由于node.js使用了引擎盖下的V8引擎,因此您可以获得有关如何改进垃圾收集A tour of V8: Garbage Collection的一些提示。如果这还不够,您可以按照these instructions强制执行GC。

答案 1 :(得分:1)

只有在某种闭合情况下才需要将变量设置为null,例如:

function createClosure(bigData) {
    var unrelatedVar = 1;

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

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

在V8中,相同级别的闭包共享相同的上下文对象,其中关闭了变量。所有相同级别的闭包然后指向上下文对象,因此它将保持活动直到所有函数都死亡。所以这里theReturnedFunctiontheCallback都是同级函数,它们都指向具有2个成员的相同上下文对象:bigData和unrelatedVar。所以很久 因为返回的函数是活动的,所以bigData也是活着的,即使它不能被引用。

这很容易陷入,因为封闭的变量看起来与局部变量完全相同,而实际上它们就像一个对象的字段(它会使用显式的this.field所以它总是如此明显)。这与在未使用之后将显式对象的.bigData字段设置为null无关,但当它是一个明确的对象时,它更难以对未命中。