在javascript中关闭和回调内存泄漏

时间:2013-05-08 13:36:46

标签: javascript node.js memory-leaks closures

function(foo, cb) {
  var bigObject = new BigObject();
  doFoo(foo, function(e) {
     if (e.type === bigObject.type) {
          cb();
          // bigObject = null;
     }
  });
}

以上示例显示了一个经典的,偶然的(或可能不是)内存泄漏闭包。 V8垃圾收集器无法确定删除bigObject是否安全,因为它在回调函数中被使用,可以多次调用。

一种解决方案是在回调函数中的作业结束时将bigObject设置为null。但是如果你使用了很多变量(想象有n变量,如bigObject,并且它们都在回调中使用)那么清理它就变成了一个难看的问题。

我的问题是:有没有其他方法来清理那些使用过的变量?

编辑这是另一个(现实世界)示例:所以我从mongodb获取应用程序并将其与其他应用程序进行比较。来自mongodb的回调使用从该回调中定义的变量应用程序。从mongodb得到结果后,我还将它作为回调返回(因为它都是异步的,我不能写回程)。所以实际上我可以将回调一直传播到源......

function compareApplications(application, condition, callback) {

    var model = database.getModel('Application');
    model.find(condition, function (err, applicationFromMongo) {
        var result = (applicationFromMongo.applicationID == application.applicationID)
        callback(result)        
    }
}

2 个答案:

答案 0 :(得分:1)

如果您的回调函数只应调用一次,那么您应该在调用它之后取消订阅。这将释放你对GC的回调+闭包。释放关闭后,GC也可以免费收集bigObject

这是最好的解决方案 - 如你所说,GC并不神奇地知道你的回调只会被调用一次。

答案 1 :(得分:0)

以Brandon的答案为基础:如果(出于某种可怕的原因)你无法取消订阅你的回调,你可以随时自行删除回调:

function createSingleUseCallback(callback)
{
    function callbackWrapper()
    {
        var ret = callback.apply(this, arguments);
        delete callback;
        return ret;
    }
    return callbackWrapper;
}

function compareApplications(application, condition, callback)
{
    var model = database.getModel('Application');
    model.find(condition, createSingleUseCallback(function (err, applicationFromMongo)
    {
        var result = (applicationFromMongo.applicationID == application.applicationID);
        callback(result);
    })
}