Сборка "мусора" jQuery - это будет чисто?

Во многих статьях (например, MSDN) было сказано, что циклическая ссылка не может быть очищена в некоторых браузерах, когда это включает Объект DOM и объект JS.

(IE 6 не может сделать, он вообще и IE7 может только сделать это между запросами страницы):

Собственный компонент JavaScript (утечки):

function leak(){
    var elem = document.createElement("DIV");
    document.body.appendChild(elem);
    elem.onclick = function () {
        elem.innerHTML = elem.innerHTML + ".";
        // ...
    };
}

Поскольку onload свойство элемента вернулось к себе посредством закрытия, оно создает циклическую ссылку:

elem [DOM] -> elem.onclick [JS] -> elem [DOM]

Версия jQuery (НЕ протекает):

function leak(){
    var elem = $('
'); $(document.body).append(elem); elem.click(function () { elem.html(elem.html() + "."); // ... }; }

В этом случае jQuery мешает утечке произойти во ВСЕХ браузерах, затронутых даже при том, что существует все еще циклическая ссылка:

elem [JS] -> element [DOM] -> elem.onclick [JS] -> elem [JS]

Мой Вопрос: Как jQuery останавливает утечку, если существует все еще циклическая ссылка?

15
задан Waynn Lue 8 March 2012 в 05:47
поделиться

3 ответа

Самая последняя вещь в коде jQuery (перед кодом для Sizzle, его механизма выбора) это (код для предотвращения утечек):

// Prevent memory leaks in IE
// Window isn't included so as not to unbind existing unload events
// More info:
//  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
if ( window.attachEvent && !window.addEventListener ) {
    window.attachEvent("onunload", function() {
        for ( var id in jQuery.cache ) {
            if ( jQuery.cache[ id ].handle ) {
                // Try/Catch is to handle iframes being unloaded, see #4280
                try {
                    jQuery.event.remove( jQuery.cache[ id ].handle.elem   );
                } catch(e) {}
            }
        }
    });
}

Когда вы делаете что-либо в jQuery, он сохраняет то, что сделал (то есть функция) и что (то есть элемент DOM). onunload проходит через кеш jQuery, удаляя функции из обработчиков событий собственного внутреннего кеша (где события все равно хранятся, а не на отдельных узлах DOM).

Да, и строка:

if ( window.attachEvent && !window.addEventListener ) {

гарантирует, что он работает только в IE.

6
ответ дан 1 December 2019 в 05:02
поделиться

переписано для дальнейшего уточнения

На самом деле в предложенном примере есть 2 причины утечки памяти. Первая утечка памяти проявляется из-за создания прямой ссылки на живой узел DOM внутри закрытия. Сборщики мусора (JS и DOM) устаревших браузеров, таких как IE6, не могут обнулить такие ссылки. Следовательно, необходимо обнулять ссылки на узлы в конце вашей функции.

jQuery обходит это по умолчанию благодаря тому, что живые элементы DOM прикрепляются к объекту jQuery в качестве атрибутов/свойств, с которыми вышеупомянутые сборщики мусора не имеют проблем в определении нулевых ссылок. Если объект jQuery имеет нулевые ссылки, он просто очищается, а вместе с ним и его атрибуты/свойства (в данном случае ссылки на живые элементы DOM).

Поэтому, чтобы избежать этой утечки памяти, нужно, чтобы объект поддерживал ссылку на живой узел DOM, а затем ссылался на этот объект в ваших закрытиях. Закрытия будут поддерживать только ссылки на объект, а не на живой элемент DOM, поскольку эта ссылка принадлежит объекту.

// will still leak, but not due to closure references, thats solved.
function noLeak(){
    var obj= {
        elem: document.createElement('div')
    }
    obj.elem.onclick = function(){
        obj.elem.innerHTML = obj.elem.innerHTML + ".";
    }
}

Это очистило наиболее очевидную циркулярную ссылку, но все равно есть утечка (onclick). Узел имеет ссылку на функцию, которая имеет ссылку на объект, который, в свою очередь, имеет ссылку на узел. Единственный способ обойти эту утечку, это изучить опыт конкурса addEvent (многие люди работали над решением этой утечки ;) ). По совпадению, необходимый код можно найти там, так что приношу свои извинения за то, что не предоставил код для этого ;)

Создание обертки для системы событий добавляет еще немного кода, но это необходимо. Основная идея заключается в том, чтобы добавить общий eventHandler, который делегирует событие кэшу/системе событий, хранящей необходимые ссылки. При событии выгрузки кэш очищается, разрушая циклические ссылки, что позволяет сборщикам мусора (JS и DOM) навести порядок в своих собственных углах.

1
ответ дан 1 December 2019 в 05:02
поделиться

JQuery может гарантировать отсутствие утечек только тогда, когда вы выполняете все свои манипуляции через библиотеку. В jQuery есть процедуры, называемые «empty» и «cleanData», которые вы можете просмотреть, чтобы точно увидеть, что происходит, но в основном код просто отделяет все, о чем он знает, от элементов DOM перед их освобождением. Эта процедура вызывается, когда вы делаете что-то вроде перезаписи содержимого элемента с помощью «.html ()» или «.load ()».

Лично я с осторожностью отношусь к таким терминам, как «гарантия» в подобной ситуации.

2
ответ дан 1 December 2019 в 05:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: