43

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


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

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

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

ответ

28

Из того, что я понимаю, управление памятью в 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, ссылки часто сохраняются для сохранения накладных расходов йот поиска - например:

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"); 
    }); 
} 

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

Edit: Как было отмечено в комментариях Эрик, вы также можете использовать этот указатель, чтобы избежать unnescessary Dóm поиск:

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

Вот пример: http://jsfiddle.net/qTu6y/8/ Вы можете использовать Chrome «Возьмите снимок» в профилировщике, чтобы увидеть, что каждый запуск этого блока кода потребляет около 20 МБ ОЗУ. (Во время тестирования я действительно попал в «Сценарий, в котором слишком много ошибок RAM на хроме») – Raynos

+0

+1 для «тех из нас, кто приходит с фона Java» ... и для ясного объяснения. – Thimmayya

+1

$ (this) .addClass будет лучше для производительности в последнем случае, так как «this» представляет собой набор элементов ядра JS DOM (который является тем, что объекты JQuery обычно обертывают с использованием шаблона адаптера), и JQ не будет снова войдите в DOM или проанализируйте каждый элемент DOM на странице в случае

15

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

Одним из преимуществ 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"); 
Смежные вопросы