28

Рассмотрим эту цитату из the Mozilla Docs on JavaScript memory leaks:DOM: почему это утечка памяти?

function addHandler() { 
    var el = document.getElementById('el'); 
    el.onclick = function() { 
     this.style.backgroundColor = 'red'; 
    } 
} 

Приведенный выше код устанавливает элемент краснеть, когда она нажата. Это также создает утечку памяти. Зачем? Потому что ссылка на el случайно попала в закрытие, созданное для анонимной внутренней функции . Это создает круговую ссылку между объектом JavaScript (функция) и собственным объектом (el).

Пожалуйста, объясните приведенные выше причины утечки простым и сжатым способом, я не получаю точную точку.

Имеет ли сайт/страницу проблему безопасности из-за утечки? Как мне их избежать? Какой еще код может вызвать утечку памяти? Как узнать, когда произошла утечка памяти?

Я абсолютный новичок в теме утечек памяти. Может кто-нибудь прояснить этот материал для меня шаг за шагом? Также может кто-то помочь мне прояснить это утверждение. «Это создает круговую ссылку между объектом JavaScript (функция) и собственным объектом (el)».

+1

http://www.javascriptkit.com/javatutors/closuresleak/, http://www.google.com/search?q=explanation+of+javascript+memory+leaks – CBroe

+1

@GrantKiely от MDN –

+0

http: //javascript.crockford.com/memory/leak.html – undefined

ответ

16

Существует два понятия, которые помогут вам понять этот пример.

1) Замыкания

Определение закрытия является то, что Every inner function enjoys access to its parent's function variables and parameters.

Когда функция addHandler() заканчивается, анонимная функция по-прежнему имеет доступ к переменной el родителя.

2) Функции = память

Каждый раз, когда вы определяете function создается новый объект. Что делает этот пример несколько запутанным, так это то, что onclick - это событие, которое может быть установлено только в элемент DOM один раз.

Значит, el.onclick = function(){}; будет просто перезаписать старую функцию?

Неверно! каждый раз, когда addHandler запускается, создается новый объект функции.

В заключение:

Каждый раз, когда функция работает она будет создавать новый объект, с крышкой, содержащей el. Увидев, что анонимная функция поддерживает доступ к el, сборщик мусора не может удалить его из памяти.

Функция anon будет поддерживать доступ к el, а el имеет доступ к функции, которая является циклической ссылкой, которая вызывает утечку памяти в IE.

+8

* «В этом случае« это »относится к el.» * ... это правда, но этот конкретный факт не имеет ничего общего с закрытием. Это свойство обработчиков событий, будь то закрытие или нет. Важен тот факт, что 'el' доступен во внутренней функции. –

+0

@ Грант Кили Феликс прав. –

+0

@FelixKling Спасибо! Я удалил эту часть из своего ответа. – gkiely

7

Всякий раз, когда вы определяете функцию в JavaScript, для нее создается execution context; этот контекст выполнения содержит ссылки на все переменные в области видимости цепи, начиная от глобального масштаба вплоть до локального масштаба:

function test() 
{ 
    var el = document.getElementById('el'); 
    el.onclick = function() { 
     // execution context of this function: el, test 
     alert('hello world'); 
    } 
} 

Когда test() делается, анонимная функция еще не переработанный, потому что теперь присвоенный элементу DOM; т.е. ссылается на на свойство элемента DOM.

В то же время сам элемент DOM также является частью контекста выполнения функции и теперь не может быть переработан из-за циклической ссылки, хотя не сразу очевидно, что он фактически используется; вы можете найти демонстрацию этого в this answer.

Тем не менее, в настоящее время, большинство двигателей JavaScript (даже те, что в IE) использует более продвинутые garbage collector, которые могут идентифицировать неиспользуемые переменные в целом гораздо лучше, используя такие методы, как mark-and-sweep или поколения/эфемерный сбор мусора.

Чтобы убедиться, что вы не столкнетесь с проблемами на любой браузер (хотя, в связи с типичной продолжительностью жизни страницы, это в основном теоретический):

document.getElementById('el').onclick = function() { 
    alert('hello world'); 
} 
+0

не смог получить это «но el также не перерабатывается, потому что это часть контекста выполнения этой функции». –

+1

@Maizere Контекст исполнения содержит (или, вернее, ссылки) 'el' и, следовательно, его нельзя перерабатывать из-за подсчета ссылок. –

+0

, поэтому он вызывает утечку памяти ??? –

1

Также смотрите more information раздел статья MS по вопросу:

Эта утечка памяти происходит из-за того, что объекты DOM являются объектами, отличными от JScript. Объекты DOM не находятся в схеме сбора мусора и разметки мусора JScript. Поэтому круговая ссылка между объектами DOM и обработчиками JScript не будет прерываться до тех пор, пока браузер полностью не разорвет страницу.

, но обратите внимание, что в отличии от того, что сказано в этой статье (память будет освобождена, когда браузер переходит на новую страницу), this article подтверждает, что ошибка в IE 6 вызвала память утечки навсегда.

1

Управление памятью в формате JavaScript обычно работает следующим образом: «до тех пор, пока это возможно, сохраните его». Это в основном парадигма, которая стоит за любой моделью памяти, управляемой мусором.

Сборщики мусора, как правило, очень хорошо разбираются в том, что они делают, они даже обнаруживают, что определенная группа элементов доступна только в этой самой группе элементов. Эти группы также называются круглыми ссылками, так как если вы будете следовать ссылкам, вы попадете в элемент, который вы уже посетили: вы запустили круг.

Однако, в вашем примере вы на самом деле есть два объекта из двух разных «миров»:

Circular references

Internet Explorer использует свою собственную схему сбора мусора для этого, отдельно от механизма, используемого JavaScript , Именно взаимодействие между ними может вызвать утечку памяти.

И это именно то, что происходит и может вызвать утечку памяти.

Смежные вопросы