在JavaScript中封闭和callback内存泄漏

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

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

一种解决方法是在callback函数中的作业结束时将bigObject设置为null 。 但是如果你使用了很多variables(假设有像bigObject这样的bigObject ,并且它们全部用于callback),那么清理这个变成了一个难题。

我的问题是这样的:有没有其他的方式来清理这些使用的variables?

编辑这里有另一个(现实世界)的例子:所以我从MongoDB中获取应用程序,并将其与其他应用程序进行比较。 mongodb的callback函数使用从该callback函数中定义的variables应用程序。 当我从mongodb得到结果后,我也将它作为callback函数返回(因为它是全部asynchronous的,我不能只写回车)。 所以实际上它可能发生,我一直传播callback到源…

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

如果您的callback函数只能被调用一次,那么您应该在被调用后取消订阅。 这将释放您的callback+closures到GC。 随着你的封闭释放, bigObject也将被GC收集。

这是最好的解决scheme – 正如你所说的,GC不会奇迹般地知道你的callback只会被调用一次。

build立在Brandon的答案上:如果(出于某种可怕的原因)您无法取消订阅您的回叫,您可以随时处理删除callback:

 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); }) }