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

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

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

我的问题是:还有其他方法可以清理那些使用过的变量吗?

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

function compareApplications(application, condition, callback) {

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

2 回答 2

2

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

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

于 2013-05-08T13:40:58.077 回答
0

以布兰登的回答为基础:如果(出于某种可怕的原因)您无法取消订阅回调,您始终可以自己处理删除回调:

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);
    })
}
于 2015-03-31T16:12:27.427 回答