Я зарегистрировал следующую ошибку Chrome , которая привела ко многим серьезным и неочевидным утечкам памяти в моем коде:
(Эти результаты используют профилировщик памяти Chrome Dev Tools , который запускает GC, а затем делает кучу снимков всего, что не было собрано.)
В приведенном ниже коде someClass
экземпляр является сборщиком мусора (хорошо):
var someClass = function() {};
function f() {
var some = new someClass();
return function() {};
}
window.f_ = f();
Но это не будет сбор мусора в этом случае (плохо):
var someClass = function() {};
function f() {
var some = new someClass();
function unreachable() { some; }
return function() {};
}
window.f_ = f();
И соответствующий скриншот:
Кажется, что замыкание (в данном случае function() {}
) сохраняет все объекты «живыми», если на объект ссылается любое другое замыкание в том же контексте, независимо от того, достижимо ли само это замыкание или нет.
Мой вопрос о сборке мусора из других браузеров (IE 9+ и Firefox). Я довольно хорошо знаком с инструментами webkit, такими как JavaScript-профилировщик кучи, но я мало знаком с инструментами других браузеров, поэтому я не смог проверить это.
В каком из этих трех случаев IE9 + и мусор Firefox будут собирать someClass
экземпляр?
unreachable
функция никогда не выполняется, поэтому на самом деле ничего не регистрируется.Ответы:
Насколько я могу судить, это не ошибка, а ожидаемое поведение.
Со страницы управления памятью в Mozilla : «Начиная с 2012 года, все современные браузеры выпускают сборщик мусора с меткой и меткой». «Ограничение: объекты должны быть явно недоступны » .
В ваших примерах, где это не удается
some
, все еще достижимо в закрытии Я попробовал два способа сделать его недоступным, и оба сработали. Либо вы устанавливаете,some=null
когда вам это больше не нужно, либо вы устанавливаете,window.f_ = null;
и оно исчезнет.Обновить
Я пробовал это в Chrome 30, FF25, Opera 12 и IE10 на Windows.
Стандарт ничего не знаю о сборке мусора не говорит, но дает некоторые подсказки о том, что должно произойти.
Таким образом, функция будет иметь доступ к среде родителя.
Итак,
some
должно быть доступно при закрытии возвращающей функции.Тогда почему это не всегда доступно?
Кажется, что Chrome и FF достаточно умны, чтобы исключить переменную в некоторых случаях, но и в Opera, и в IE
some
переменная доступна в замыкании (примечание: для просмотра этого установите точку остановаreturn null
и проверьте отладчик).GC можно улучшить, чтобы определить,
some
используется ли он в функциях или нет, но это будет сложно.Плохой пример:
В приведенном выше примере GC не может узнать, используется ли переменная или нет (код протестирован и работает в Chrome30, FF25, Opera 12 и IE10).
Память освобождается, если ссылка на объект прерывается путем присвоения другого значения
window.f_
.На мой взгляд, это не ошибка.
источник
setTimeout()
обратного вызова эта область функцииsetTimeout()
обратного вызова завершается, и вся эта область должна собираться мусором, освобождая свою ссылку наsome
. Больше нет кода, который может быть запущен, чтобы достичь экземпляраsome
в замыкании. Это должен быть мусор. Последний пример еще хуже, потому чтоunreachable()
его даже не вызывают, и никто не имеет на него ссылку. Его сфера должна быть также GCed. Оба они кажутся ошибками. В JS не требуется языка для «освобождения» вещей в области действия функции.f()
вызван, больше нет никаких реальных ссылок наsome
. Это недоступно и должно быть GCed.eval
это действительно особый случай. Например,eval
нельзя использовать псевдонимы ( developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… ), напримерvar eval2 = eval
. Еслиeval
используется (и поскольку он не может быть вызван другим именем, что легко сделать), то мы должны предположить, что он может использовать что-либо в области видимости.Я проверял это в IE9 + и Firefox.
Живой сайт здесь .
Я надеялся получить массив из 500
function() {}
с минимальным объемом памяти.К сожалению, это было не так. Каждая пустая функция содержит (навсегда недостижимый, но не GC) массив из миллиона чисел.
В конечном итоге Chrome останавливается и умирает, Firefox завершает все это после использования почти 4 ГБ ОЗУ, а IE асимптотически замедляется до тех пор, пока не появится сообщение «Недостаточно памяти».
Удаление одной из закомментированных строк исправляет все.
Кажется, что все три из этих браузеров (Chrome, Firefox и IE) хранят записи среды для контекста, а не для закрытия. Борис выдвигает гипотезу, что причиной этого решения является производительность, и это кажется вероятным, хотя я не уверен, насколько эффективным оно может быть названо в свете вышеупомянутого эксперимента.
Если нужна ссылка на закрытие
some
(если я не использовал ее здесь, но представьте, что я использовал), если вместоя использую
это исправит проблемы с памятью, переместив замыкание в другой контекст, чем моя другая функция.
Это сделает мою жизнь намного более утомительной.
PS Из любопытства я попробовал это на Java (используя его способность определять классы внутри функций). GC работает так, как я изначально надеялся на Javascript.
источник
Эвристика может быть разной, но общий способ реализации такого рода вещей заключается в создании записи среды для каждого вызова
f()
в вашем случае и сохранении только тех локальных объектовf
, которые фактически закрыты ( некоторым закрытием) в этой записи среды. Тогда любое замыкание, созданное в вызовеf
сохраняет запись среды. Я считаю, что именно так Firefox реализует замыкания, по крайней мере.Это дает преимущества быстрого доступа к закрытым переменным и простоты реализации. У него есть недостаток наблюдаемого эффекта, когда короткоживущее замыкание, закрывающееся по некоторой переменной, заставляет его поддерживаться долгоживущим замыканием.
Можно попытаться создать несколько записей среды для разных замыканий, в зависимости от того, что они на самом деле закрывают, но это может очень быстро усложниться и может вызвать проблемы с производительностью и памятью.
источник
O(n^2)
илиO(2^n)
как взрыв, но не пропорциональный рост.как добавить (5); // возвращает 5
Добавить (20); // возвращает 25 (5 + 20)
Добавить (3); // возвращает 28 (25 + 3)
это можно сделать двумя способами: во-первых, обычно определяют глобальную переменную. Конечно, вы можете использовать глобальную переменную для хранения итогового значения. Но имейте в виду, что этот чувак съест вас заживо, если вы (ab) будете использовать глобалы.
теперь последний способ использования замыкания без определения глобальной переменной
источник
источник
источник