Сборка мусора JQuery - это будет чисто? - PullRequest
15 голосов
/ 03 августа 2010

Во многих статьях (например, 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 = $('<div></div>');
    $(document.body).append(elem);
    elem.click(function () {
        elem.html(elem.html() + ".");
        // ...
    };
}

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

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

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

Ответы [ 3 ]

6 голосов
/ 11 августа 2010

Самая последняя вещь в коде 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).

Oh, и строка:

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

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

2 голосов
/ 03 августа 2010

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

Лично я очень осторожен с такими терминами, как "гарантия" в такой ситуации.

1 голос
/ 03 августа 2010

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

На самом деле в предлагаемом примере есть 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, который делегирует событие в кэш / систему событий, в которой хранятся необходимые ссылки.При событии unload кэш очищается, разбивая циклические ссылки, позволяя сборщикам мусора (JS и DOM) убирать свои собственные углы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...