Точное объяснение JavaScript ↔ Справочная проблема DOM

Одно из рекомендуемых преимуществ jQuery.data по сравнению с необработанными свойствами expando (произвольные атрибуты, которые вы можете назначить узлам DOM) состоит в том, что jQuery.data является "безопасным от циклических ссылок и, следовательно, свободным от утечек памяти". Статья из Google под названием "Оптимизация кода JavaScript" приводится более подробно:

Наиболее распространенные утечки памяти для веб-приложений включают в себя круговые ссылки между движком JavaScript script и браузерами С++ объекты, реализующие DOM (например, между JavaScript scriptдвигателя и Internet Explorer COM, или между JavaScript-движок и инфраструктура Firefox XPCOM).

В нем перечислены два примера круговых эталонных шаблонов:

  • Элемент DOM → обработчик событий → область охвата → DOM

  • Элемент DOM → через expando → промежуточный объект → Элемент DOM

Однако, если опорный цикл между DOM node и объектом JavaScript вызывает утечку памяти, не означает ли это, что любой нетривиальный обработчик событий (например, onclick) приведет к такой утечке? Я не вижу, как это возможно для обработчика событий, чтобы избежать эталонного цикла, потому что я его вижу:

  • Элемент DOM ссылается на обработчик событий.

  • Обработчик событий ссылается на DOM (прямо или косвенно). В любом случае почти невозможно избежать ссылки window в любом интересном обработчике событий, не записывая цикл setInterval, который читает действия из глобальной очереди.

Может ли кто-то дать точное описание JavaScript & harr; DOM круговая справочная проблема? Что я хотел бы уточнить:

  • Какие браузеры выполняются? Комментарий в источнике jQuery специально упоминает IE6-7, но статья Google предполагает, что Firefox также затронут.

  • Являются ли свойства expando и обработчики событий как-то разными в отношении утечек памяти? Или оба эти фрагмента кода восприимчивы к утечке памяти такого же типа?

    // Create an expando that references to its own element.
    var elem = document.getElementById('foo');
    elem.myself = elem;
    
    // Create an event handler that references its own element.
    var elem = document.getElementById('foo');
    elem.onclick = function() {
        elem.style.display = 'none';
    };
    
  • Если страница теряет память из-за циклической ссылки, сохраняется ли утечка до закрытия всего приложения браузера или освобождается ли память при закрытии окна/вкладки?

Ответ 1

Вероятно, не стоит воспроизводить весь контент в этих ссылках, поэтому я бы посоветовал вам немного прочитать и посмотреть другие поисковые запросы Google:

javascript, круговые ссылки и утечки памяти

Знаете ли вы, что может вызвать утечку памяти в JavaScript?

http://www.ibm.com/developerworks/web/library/wa-memleak/

http://www.ibm.com/developerworks/web/library/wa-sieve/index.html?ca=drs-

http://code.google.com/p/google-web-toolkit/wiki/UnderstandingMemoryLeaks

Наихудшие утечки памяти находятся в IE6, где утечки являются постоянными (даже после того, как вы покинули затронутую веб-страницу). Другие утечки, как правило, только тогда, когда вы находитесь на этой конкретной странице и очищаетесь, когда покидаете страницу.

Дело в том, что браузер должен иметь возможность обрабатывать циклические ссылки. Он должен был видеть, что, хотя элемент DOM все еще ссылается на элемент JavaScript, сам элемент JavaScript существует только потому, что элемент DOM все еще жив и, следовательно, нет истинной внешней ссылки, оставленной для элемента DOM, Именно это признание привело к тому, что старые версии IE были плохими. Таким образом, в ваших ссылках на код, которые связаны с обработчиками событий, сборщик мусора должен быть достаточно умным, чтобы знать, что единственные ссылки, оставленные элементу DOM в JavaScript, - это ссылки, которые сами уйдут, когда элемент DOM и обработчики событий будут удалены - таким образом нет истинных внешних ссылок, поэтому безопасно удалять как элемент DOM, так и обработчик событий. Это более сложная версия общей циркулярной справочной проблемы, которую все сборщики мусора должны обрабатывать, когда объект A относится к объекту B, а объект B относится к объекту A, но ни один другой объект не относится ни к A, ни к B, поэтому оба могут быть освобождены.

jQuery .data() делает вещи более надежными, поскольку более старые версии IE имели определенную проблему со свойствами, которые были добавлены в элемент DOM, и не обрабатывали циклические ссылки правильно, используя данные в этих свойствах и, следовательно, не освобождали бы вещи, когда он должен иметь (утечку). .data() работает вокруг этого, используя только одно добавленное свойство на элементе DOM, который является безопасной, не утечкой. Эта строка является ключом к объекту JavaScript, который может содержать все свойства, которые вы хотите связать с элементом DOM. Поскольку эти свойства хранятся в обычном JavaScript, где в браузерах нет круговых опорных ошибок, выполнение этого способа не вызывает утечек.

Важно понять, что все еще могут быть некоторые круговые ссылки и что ОК. Обход - это перемещение круглых ссылок в место, где браузеры обрабатывают их надлежащим образом, вместо того, чтобы помещать их туда, где у браузеров есть ошибки.