Подтвердить что ты не робот

Проблемы с утечкой памяти jQuery и причины

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


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

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

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

4b9b3361

Ответ 1

Из того, что я понимаю, управление памятью в 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");
    });
}

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

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

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

Ответ 2

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

Одна из сильных сторон 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");