Закрытие и обратная ошибка утечки в javascript
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
, и все они используются в обратном вызове), то очистка становится уродливой проблемой.
Мой вопрос таков: есть ли другой способ очистить используемые переменные?
EDIT Вот еще один пример (реальный мир): поэтому я получаю приложение от mongodb и сравниваю его с каким-либо другим приложением. Обратный вызов от mongodb использует переменное приложение, которое определено из этого обратного вызова. После получения результата от mongodb я возвращаю его также как обратный вызов (потому что это все async, и я не могу просто написать 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)
}
}
Ответы
Ответ 1
Если ваша функция обратного вызова должна быть вызвана один раз, вам следует отказаться от подписки после ее вызова. Это освободит ваш обратный вызов + закрытие GC. С закрытием вашего закрытия, bigObject
также будет бесплатным для сбора GC.
Это лучшее решение - как вы отметили, GC не волшебным образом знает, что ваш обратный вызов будет вызываться только один раз.
Ответ 2
Чтобы опираться на ответ Брэндона: если (по какой-то страшной причине) вы не можете отказаться от отзыва своего обратного вызова, вы всегда можете справиться с удалением обратного вызова:
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);
})
}