Поиск утечек памяти в JavaScript с помощью Chrome

Я создал очень простой тестовый пример, который создает представление Backbone, прикрепляет обработчик к событию и создает пользовательский класс. Я считаю, что нажав кнопку "Удалить" в этом примере, все будет очищено и не должно быть утечек памяти.

jsfiddle для кода здесь: http://jsfiddle.net/4QhR2/

// scope everything to a function
function main() {

    function MyWrapper() {
        this.element = null;
    }
    MyWrapper.prototype.set = function(elem) {
        this.element = elem;
    }
    MyWrapper.prototype.get = function() {
        return this.element;
    }

    var MyView = Backbone.View.extend({
        tagName : "div",
        id : "view",
        events : {
            "click #button" : "onButton",
        },    
        initialize : function(options) {        
            // done for demo purposes only, should be using templates
            this.html_text = "<input type='text' id='textbox' /><button id='button'>Remove</button>";        
            this.listenTo(this,"all",function(){console.log("Event: "+arguments[0]);});
        },
        render : function() {        
            this.$el.html(this.html_text);

            this.wrapper = new MyWrapper();
            this.wrapper.set(this.$("#textbox"));
            this.wrapper.get().val("placeholder");

            return this;
        },
        onButton : function() {
            // assume this gets .remove() called on subviews (if they existed)
            this.trigger("cleanup");
            this.remove();
        }
    });

    var view = new MyView();
    $("#content").append(view.render().el);
}

main();

Однако я не понимаю, как использовать профилировщик Google Chrome, чтобы убедиться, что это, по сути, случай. Есть gazillion вещи, которые появляются на снимке профайлера кучи, и я понятия не имею, как декодировать, что хорошо/плохо. Учебники, которые я видел на нем, пока либо просто говорят мне "использовать профилировщик моментальных снимков", либо дать мне подробный манифест о том, как работает весь профайлер. Можно ли просто использовать профилировщик в качестве инструмента, или мне действительно нужно понять, как все это было спроектировано?

EDIT: Учебники, подобные этим:

Исправление утечки памяти Gmail

Использование DevTools

Являются представителем какого-то более сильного материала там, из того, что я видел. Однако, невзирая на концепцию 3 Snapshot Technique, я нахожу, что они предлагают очень мало практических знаний (для новичка, подобного мне). Учебник "Использование DevTools" не работает на реальном примере, поэтому его неопределенное и общее концептуальное описание вещей не слишком полезно. Что касается примера Gmail:

Итак, вы обнаружили утечку. Теперь что?

  • Изучите путь удержания просочившихся объектов в нижней половине панели "Профили".

  • Если сайт распределения не может быть легко выведен (например, прослушиватели событий):

  • Инструмент конструктор сохраняющего объекта через консоль JS для сохранения трассировки стека для распределений

  • Использование закрытия? Включите соответствующий существующий флаг (т.е. Goog.events.Listener.ENABLE_MONITORING), чтобы установить свойство createStack во время построения

Я больше смущен, прочитав это, не меньше. И, опять же, это просто говорит мне делать что-то, а не как их делать. С моей точки зрения, вся информация там слишком расплывчата или будет иметь смысл только для того, кто уже понял этот процесс.

Некоторые из этих более конкретных проблем были подняты в ответе @Джонатан Нагуин ниже.

Ответ 1

Хороший рабочий процесс для обнаружения утечек памяти - это метод трех снимок, впервые используемый Лореей Ли и командой Gmail для решения некоторых проблем с памятью. Обычно этапы:

  • Сделайте снимок кучи.
  • Сделайте что-нибудь.
  • Сделайте еще один снимок кучи.
  • Повторите то же самое.
  • Сделайте еще один снимок кучи.
  • Фильтровать объекты, выделенные между моментальными снимками 1 и 2 в снимке 3 "Сводка".

В вашем примере я адаптировал код, чтобы показать этот процесс (вы можете найти его здесь), задерживая создание Backbone View до тех пор, пока событие щелчка кнопки "Пуск". Сейчас:

  • Запустите HTML (сохраненный локально с помощью этого address) и сделайте снимок.
  • Нажмите "Пуск", чтобы создать представление.
  • Сделайте еще один снимок.
  • Нажмите "Удалить".
  • Сделайте еще один снимок.
  • Фильтровать объекты, выделенные между моментальными снимками 1 и 2 в снимке 3 "Сводка".

Теперь вы готовы найти утечки памяти!

Вы увидите узлы нескольких разных цветов. У красных узлов нет прямых ссылок от Javascript к ним, но они живы, потому что они являются частью отдельного дерева DOM. Может быть node в дереве, на который ссылается Javascript (возможно, как закрытие или переменная), но по совпадению предотвращает сбор всего дерева DOM.

enter image description here

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

В вашем конкретном разделе вы можете увидеть элемент HTML Div, отмеченный как красный. Если вы разберете элемент, вы увидите, что на него ссылается функция "кеш".

enter image description here

Выберите строку и в вашей консоли тип $0, вы увидите фактическую функцию и местоположение:

>$0
function cache( key, value ) {
        // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
        if ( keys.push( key += " " ) > Expr.cacheLength ) {
            // Only keep the most recent entries
            delete cache[ keys.shift() ];
        }
        return (cache[ key ] = value);
    }                                                     jquery-2.0.2.js:1166

Здесь указывается ваш элемент. К сожалению, вы ничего не можете сделать, это внутренний механизм jQuery. Но, только для целей тестирования, перейдите к функции и измените метод на:

function cache( key, value ) {
    return value;
}

Теперь, если вы повторите этот процесс, вы не увидите красный node:)

Документация:

Ответ 2

Вот совет по профилированию памяти jsfiddle: используйте следующий URL-адрес, чтобы изолировать результат jsfiddle, он удаляет всю фреймворк jsfiddle и загружает только ваш результат.

http://jsfiddle.net/4QhR2/show/

Я никогда не мог понять, как использовать Timeline и Profiler для отслеживания утечек памяти, пока не прочитаю следующую документацию. После прочтения раздела, озаглавленного "Трекер выделения объектов", я смог использовать инструмент "Записывать кучи памяти" и отслеживать некоторые отдельные узлы DOM.

Я исправил проблему, переключившись с привязки события jQuery, на использование делегирования событий Backbone. Я понимаю, что новые версии Backbone автоматически отключают события для вас, если вы вызываете View.remove(). Выполните некоторые из демонстраций самостоятельно, они настроены с утечками памяти для вас, чтобы идентифицировать. Не стесняйтесь задавать вопросы здесь, если вы все еще не получите его после изучения этой документации.

https://developers.google.com/chrome-developer-tools/docs/javascript-memory-profiling

Ответ 3

В основном вам нужно посмотреть количество объектов внутри моментального снимка кучи. Если количество объектов увеличивается между двумя моментальными снимками, и вы удаляете объекты, тогда у вас есть утечка памяти. Мой совет - искать обработчики событий в своем коде, которые не отделяются.

Ответ 4

Вы также можете прочитать:

http://addyosmani.com/blog/taming-the-unicorn-easing-javascript-memory-profiling-in-devtools/

В нем объясняется использование инструментов разработчика Chrome и даются некоторые пошаговые рекомендации о том, как подтвердить и найти утечку памяти, используя сравнение моментальных снимков кучи и различные доступные снимки снимков hep.

Ответ 5

В Google есть ознакомительное видео, которое будет очень полезно для поиска утечек памяти JavaScript.

https://www.youtube.com/watch?v=L3ugr9BJqIs

Ответ 6

Вы также можете посмотреть вкладку "Временная шкала" в инструментах разработчика. Запишите использование вашего приложения и следите за DOM Node и счетчиком прослушивания событий.

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

Ответ 7

Во-вторых, совет, чтобы сделать снимок кучи, они отлично подходят для обнаружения утечек памяти, хром отлично справляется с моментальным снимком.

В моем исследовательском проекте для моей степени я создавал интерактивное веб-приложение, которое должно было генерировать много данных, собранных в "слоях", многие из этих уровней были бы "удалены" в пользовательском интерфейсе, но по какой-то причине память не был освобожден, с помощью инструмента моментального снимка я смог определить, что JQuery сохранял ссылку на объект (источник был, когда я пытался запустить событие .load(), которое сохраняло ссылку, несмотря на выход из области), Имея эту информацию в ручном режиме, я сохранил свой проект, это очень полезный инструмент, когда вы используете библиотеки других людей, и у вас есть эта проблема с затяжными ссылками, которые останавливают выполнение GC.

EDIT: Также полезно заранее планировать, какие действия вы собираетесь выполнять, чтобы свести к минимуму время, затраченное на моментальные снимки, выдвинуть гипотезу о том, что может вызвать проблему, и протестировать каждый сценарий, сделать снимки до и после.