Шаблоны и причины утечек памяти в jQuery - PullRequest
43 голосов
/ 18 февраля 2011

Каковы некоторые стандартные проблемы или шаблоны кодирования в jQuery, которые приводят к утечкам памяти?


Я видел несколько вопросов, связанных с вызовом ajax () или удалением jsonp или DOM в StackOverflow.Большинство вопросов утечки памяти в jQuery сосредоточены на конкретных проблемах или браузерах, и было бы неплохо иметь список стандартных шаблонов утечки памяти в jQuery.

Вот несколько связанных вопросов по SO:

Ресурсы в Интернете:

Ответы [ 2 ]

28 голосов
/ 30 марта 2011

Насколько я понимаю, управление памятью в javascript осуществляется путем подсчета ссылок - хотя ссылка на объект все еще существует, она не будет освобождена.Это означает, что создание утечки памяти в одностраничном приложении тривиально и может привести к сбоям в использовании из-за Java-фона.Это не относится к JQuery.Возьмите следующий код, например:

function MyObject = function(){
   var _this = this;
   this.count = 0;
   this.getAndIncrement = function(){
       _this.count++;
       return _this.count;
   }
}

for(var i = 0; i < 10000; i++){
    var obj = new MyObject();
    obj.getAndIncrement();
}

Это будет выглядеть нормально, пока вы не посмотрите на использование памяти.Экземпляры MyObject никогда не освобождаются, пока страница активна, из-за указателя «_this» (увеличьте максимальное значение i, чтобы увидеть его более резко).(В более старых версиях IE они никогда не освобождались до выхода из программы.) Поскольку объекты javascript могут совместно использоваться кадрами (я не рекомендую делать это, поскольку это серьезно темпераментно.), Бывают случаи, когда даже в современном браузере javascriptобъекты могут зависать намного дольше, чем им предназначено.

В контексте jquery ссылки часто сохраняются для экономии накладных расходов при поиске в dom - например:

function run(){
    var domObjects = $(".myClass");
    domObjects.click(function(){
        domObjects.addClass(".myOtherClass");
    });
}

Этокод будет держаться за domObject (и все его содержимое) вечно из-за ссылки на него в функции обратного вызова.

Если авторы jquery пропустили подобные экземпляры внутренне, то сама библиотека утечет,но чаще это клиентский код.

Второй пример можно исправить, явно очистив указатель, когда он больше не требуется:

function run(){
    var domObjects = $(".myClass");
    domObjects.click(function(){
        if(domObjects){
            domObjects.addClass(".myOtherClass");
            domObjects = null;
        }
    });
}

или выполнив поиск снова:

function run(){
    $(".myClass").click(function(){
        $(".myClass").addClass(".myOtherClass");
    });
}

Хорошее практическое правило - соблюдать осторожность при определении функций обратного вызова и избегатьслишком много вложений, где это возможно.

Редактировать: Как было отмечено в комментариях Эрика, вы также можете использовать указатель this, чтобы избежать ненужного поиска в dom:

function run(){
    $(".myClass").click(function(){
        $(this).addClass(".myOtherClass");
    });
}
16 голосов
/ 16 мая 2014

Я внесу здесь один антишаблон, который представляет собой утечку «средней цепи».

Одной из сильных сторон jQuery является его API цепочки, который позволяет вам продолжать изменять, фильтровать и манипулироватьэлементы:

$(".message").addClass("unread").find(".author").addClass("noob");

В конце этой цепочки у вас есть объект jQuery со всеми элементами ".message .author", но , на который ссылается объект , и объект соригинальные элементы ".message".Вы можете получить к ним метод .end() и что-то с ними сделать:

 $(".message")
   .find(".author")
     .addClass("prolific")
   .end()
   .addClass("unread");

Теперь при использовании этого способа проблем с утечками нет.Однако, если вы присваиваете результат цепочки переменной, имеющей длительный срок службы, обратные ссылки на более ранние наборы сохраняются и не могут быть собраны сборщиком мусора, пока переменная не выйдет из области видимости.Если эта переменная является глобальной, ссылки никогда не выходят за рамки.

Так, например, предположим, что вы прочитали в каком-то посте в блоге 2008 года, что $("a").find("b") был "более эффективным", чем $("a b") (хотя егоне стоит даже думать о такой микрооптимизации).Вы решаете, что вам нужен глобальный элемент для хранения списка авторов, поэтому вы делаете это:

authors = $(".message").find(".author");

Теперь у вас есть объект jQuery со списком авторов, но он также ссылается на jQueryобъект, который является полным списком сообщений.Вы, вероятно, никогда не будете использовать его или даже будете знать, что он там, и он занимает память.

Обратите внимание, что утечки могут происходить только с методами, которые выбирают новые элементы из существующего набора, такие как.find, .filter, .children и т. Д. Документы указывают, когда возвращается новый набор.Простое использование API цепочки не приводит к утечке, если цепочка имеет простые нефильтрующие методы, такие как .css, так что все в порядке:

authors = $(".message .author").addClass("prolific");
...