Backbone.js Управление памятью, увеличение DOM Node Count
Ситуация. Я работаю над довольно прилично сложным одностраничным Backbone-приложением, которое потенциально может работать в течение 8-12 часов. Из-за этого необходимо обеспечить, чтобы приложение не просачивалось и не имело репутации сбоя после X часов или резко сократилось.
Приложение: приложение построено на Backbone (mv *), Zepto (похоже на jquery), Curl (amd loader) и Mustache (templating).
Проблема. Я только что победил слушателей событий. Кажется, что сборщик мусора отлично справляется с этими парнями, , но DOM Node Count не перестанет лазать.
Вопросы:
- Есть ли способ утилизации узлов DOM, чтобы они были правильно собраны мусором или это DOM Node Подсчитайте текущую сумму, которая никогда не уменьшится?
- Кто-нибудь знает какие-либо из этих фреймворков, чтобы плохо обрабатывать узлы DOM? Возможно Усы?
- Является ли DOM Node Count даже надежной цифрой?
Я действительно просто ищу начало своего приключения, чтобы остановить рост DOM Nodes. Любая помощь или руководство были бы с благодарностью оценены (и соответственно поддержаны).
Я предположил, что после того, как прослушиватели событий были правильно удалены, DOM Node Count будет просто управлять собой, но это, похоже, не так.
Испытания
![Poorly Managed DOM Node Count]()
- Первый тест: 6,8 минуты, 110 000 узлов DOM.
Изменить. Без записи по временной шкале я повторяю один и тот же script случайным образом перепутал ссылки и сделал снимок экрана с отметкой в 7 минут. После того, как GC прошел, у меня были эти результаты.
![Poorly Managed DOM Node Count]()
- Второй тест: 7.1 минут, 141 000 узлов DOM (без записи временной шкалы)
Изменить: после исправления:
После обновления магистрали и использования listenTo и stopListening везде
- 7 минут: 6,926 узлов DOM (см. примечание ниже).
- 20 минут: 6 000 узлов DOM, 20 слушателей событий, память 20 МБ.
- 25 минут: 11 600 узлов DOM, 44 прослушивателя, память 21,7 МБ.
- 28 минут: 9 000 узлов DOM, 22 прослушивателя событий, память 21,7 МБ.
- 30 минут: 13 700 узлов DOM, 123 прослушивателей событий, память 21.7.
- 31 минута: 7 040 узлов DOM, 30 прослушивателей, память 21.7.
Ответы
Ответ 1
Я предположил, что после того, как прослушиватели событий были правильно удалены, DOM Node Count будет просто управлять собой, но это, похоже, не так.
Если я понял, что вы пытаетесь избавиться от Node, удалив из него слушателей, это тот случай?
Обратите внимание, что добавление прослушивателя событий в DOM Node не предотвращает сбор мусора Node, зависимость находится в противоположном направлении: пока Node жив, функция слушателя не будет собрана.
- Существует ли подходящий способ утилизации узлов DOM, чтобы они были правильно собраны мусором или это DOM Node Подсчитайте текущую сумму, которая никогда не уменьшится?
Чтобы убедиться, что DOM Node может быть собран с помощью мусора, вы должны
- Удалите Node из дерева документов.
- Очистить все ссылки от javascript до Node AND ко всем узлам в том же поддереве, что и ссылка с javascript на один из узлов в поддереве, будет содержать целые поддеревы.
Поэтому недостаточно просто удалить слушателей из Node, чтобы сделать его доступным для коллекционирования. Кроме того, не требуется для удаления слушателей из Node, если вы хотите, чтобы Node был собран.
Счет DOM Node должен уменьшаться, когда некоторые узлы собираются GC и уничтожаются. Число указывает текущее количество узлов DOM, которые были созданы, но не уничтожены, поэтому он не должен расти неограниченно, если нет утечки памяти.
- Является ли DOM Node Count даже надежной цифрой?
Да. Он должен быть надежным, так как он увеличивается, когда создается новый DOM Node и уменьшается при его уничтожении. Таким образом, реализация довольно проста, чтобы доверять ей.
Ответ 2
Это исправлено! - UPGRADE BACKBONE. (продолжить чтение)
Мы обновили с Backbone 0.9.2 до Backpone 0.9.10 и реализовали функцию listenTo и stopListening для каждого вида/модели/коллекции. Результаты OMGFANTASTIC.
После 7 минут работы с тем же стресс-тестом, это результаты:
![enter image description here]()
Результаты: 7,0 минут, 6,926 узлов DOM (без записи временной шкалы) и счетчика прослушивания событий выглядит как BLUE BLADES OF GRASS. Я в шоке. Использование памяти также удивительно мало по сравнению с предыдущими тестами.
Через 18 минут: количество слушателей событий одинаково, никогда не превышая 154, а DOM Node Count остается ниже 25 000 за все время! Очевидно, что некоторые вещи ускользают (некоторые не-опорные компоненты, которые все еще используются, наиболее вероятно), но улучшение поражает.
Заключение. До этой версии Backbone мы не очень хорошо работали с прослушивателями в самой Backbone. Слушатели DOM обрабатывались отлично, но не между моделями/представлениями/коллекциями. Многие связанные обратные вызовы были привязаны к Backbone Views, которые, как я полагаю, не позволили сборщику мусора освободить узлы DOM. Многие опрокидывающие события/обратные вызовы в рамках Backbone (а не только привязка к DOM) создают множество разрывающих узлов DOM, которые не могут быть собраны в мусор.
Если это не достаточно хорошая причина для обновления Backbone, я не знаю, что это такое: o
Ответ 3
Рост числа узлов DOM является основным признаком утечки памяти (обычно в коде нашей страницы).
Поэтому вам нужно бороться с этим. Стандартная методика описана в ответе на этот вопрос.
В этом снимке слишком много деталей. И эта схема 3 снимков помогает отфильтровать не интересную часть моментального снимка и показывает только кандидатов на утечку.
Пожалуйста, убедитесь, что вы используете последнюю версию хром, например, Chrome Canary.
Это должен быть свежий экземпляр с одной вкладкой без расширений.
Было бы неплохо иметь никаких сообщений об ошибках в консоли, без точек останова и не останавливаться на исключении, потому что все это может повлиять на страницу и, как результат, содержимое моментального снимка.
Этот пост может быть вам интересен.
Ответ 4
Я нашел другой способ избежать jank
render: function() {
this.$el.empty();
var container = document.createDocumentFragment();
// render each subview, appending to our root element
_.each(this._views, function(subview) {
container.appendChild(subview.render().el)
});
this.$el.append(container);
}
см. здесь http://ozkatz.github.io/avoiding-common-backbonejs-pitfalls.html