Исправить проблемы с памятью

Узнайте, как использовать Chrome и DevTools для выявления проблем с памятью, влияющих на производительность страниц, включая утечки памяти, ее раздувание и частую сборку мусора.

Краткое содержание

  • Узнайте, сколько памяти использует ваша страница, с помощью диспетчера задач Chrome.
  • Визуализируйте использование памяти с течением времени с помощью записей временной шкалы.
  • Выявляйте отдельные деревья DOM (частая причина утечек памяти) с помощью снимков кучи.
  • Узнайте, когда в вашей куче JS выделяется новая память, с помощью записей временной шкалы распределения.
  • Определите отдельные элементы, сохраненные ссылкой на JavaScript.

Обзор

В духе модели производительности RAIL в центре внимания ваших усилий по повышению производительности должны быть ваши пользователи.

Проблемы с памятью важны, поскольку они часто заметны пользователям. Пользователи могут воспринимать проблемы с памятью следующим образом:

  • Производительность страницы со временем становится все хуже. Возможно, это признак утечки памяти. Утечка памяти — это когда ошибка на странице приводит к тому, что страница со временем использует все больше и больше памяти.
  • Производительность страницы постоянно низкая. Возможно, это признак раздутия памяти. Раздувание памяти — это когда страница использует больше памяти, чем необходимо для оптимальной скорости страницы.
  • Производительность страницы задерживается или часто приостанавливается. Возможно, это признак частой сборки мусора. Сбор мусора — это когда браузер освобождает память. Браузер решает, когда это произойдет. Во время сбора данных выполнение всех сценариев приостанавливается. Поэтому, если браузер много собирает мусор, выполнение скрипта будет часто приостанавливаться.

Раздувание памяти: насколько это «слишком много»?

Утечку памяти легко определить. Если сайт постепенно использует все больше и больше памяти, значит, у вас есть утечка. Но раздувание памяти определить немного сложнее. Что квалифицируется как «использование слишком большого количества памяти»?

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

Ключевым моментом здесь является использование модели RAIL и сосредоточение внимания на своих пользователях. Узнайте, какие устройства популярны среди ваших пользователей, а затем протестируйте свою страницу на этих устройствах. Если работа постоянно плохая, возможно, страница превышает возможности памяти этих устройств.

Контролируйте использование памяти в режиме реального времени с помощью диспетчера задач Chrome.

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

  1. Нажмите Shift+Esc или перейдите в главное меню Chrome и выберите «Дополнительные инструменты» > «Диспетчер задач» , чтобы открыть диспетчер задач.

    Открытие диспетчера задач.

  2. Щелкните правой кнопкой мыши заголовок таблицы диспетчера задач и включите память JavaScript .

    Включение памяти JS в заголовке диспетчера задач.

Эти два столбца расскажут вам разные вещи о том, как ваша страница использует память:

  • Столбец «Объем памяти» представляет память ОС. Узлы DOM хранятся в памяти ОС. Если это значение увеличивается, создаются узлы DOM.
  • Столбец «Память JavaScript» представляет собой кучу JS. Этот столбец содержит два значения. Значение, которое вас интересует, — это действующее число (число в скобках). Живое число показывает, сколько памяти используют доступные объекты на вашей странице. Если это число увеличивается, либо создаются новые объекты, либо растут существующие объекты.

    Диспетчер задач с включенным заголовком памяти Javascript.

Визуализация утечек памяти с помощью записей производительности

Вы также можете использовать панель «Производительность» в качестве еще одной отправной точки в своем расследовании. Панель «Производительность» помогает визуализировать использование памяти страницы с течением времени.

  1. Откройте панель «Производительность» в DevTools.
  2. Установите флажок Память .
  3. Сделайте запись .

Чтобы продемонстрировать записи в памяти производительности , рассмотрите следующий код:

var x = [];

function grow() {
  for (var i = 0; i < 10000; i++) {
    document.body.appendChild(document.createElement('div'));
  }
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

Каждый раз, когда нажимается кнопка, указанная в коде, к телу документа добавляются десять тысяч узлов div , а в массив x помещается строка из миллиона символов x . Запуск этого кода создает запись временной шкалы, как показано на следующем снимке экрана:

Простой пример роста.

Во-первых, объяснение пользовательского интерфейса. График HEAP на панели «Обзор» (под NET ) представляет собой кучу JS. Под панелью «Обзор» находится панель «Счетчик» . Здесь вы можете увидеть использование памяти с разбивкой по куче JS (так же, как график HEAP на панели «Обзор» ), документам, узлам DOM, прослушивателям и памяти графического процессора. Отключение флажка скрывает его от графика.

Теперь анализ кода по сравнению со скриншотом. Если вы посмотрите на счетчик узлов (зеленый график), вы увидите, что он полностью соответствует коду. Количество узлов увеличивается дискретными шагами. Вы можете предположить, что каждое увеличение количества узлов — это вызов функции grow() . График кучи JS (синий график) не так прост. В соответствии с лучшими практиками первое погружение на самом деле представляет собой принудительную сборку мусора (выполняется нажатием кнопки «Сбор мусора» ). По мере записи вы можете видеть, что размер кучи JS резко увеличивается. Это естественно и ожидаемо: код JavaScript создает узлы DOM при каждом нажатии кнопки и выполняет большую работу, когда создает строку из миллиона символов. Ключевым моментом здесь является тот факт, что куча JS заканчивается выше, чем началась («начало» здесь — это точка после принудительной сборки мусора). В реальном мире, если бы вы увидели эту закономерность увеличения размера кучи JS или размера узла, это потенциально могло бы означать утечку памяти.

Обнаружение утечек памяти в отдельном дереве DOM с помощью снимков кучи

Узел DOM может быть подвергнут сборке мусора только в том случае, если на него нет ссылок ни из дерева DOM страницы, ни из кода JavaScript. Узел считается «отсоединенным», если он удален из дерева DOM, но некоторый JavaScript все еще ссылается на него. Отсоединенные узлы DOM являются распространенной причиной утечек памяти. В этом разделе вы узнаете, как использовать профилировщики кучи DevTools для идентификации отдельных узлов.

Вот простой пример отдельных узлов DOM.

var detachedTree;

function create() {
  var ul = document.createElement('ul');
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li');
    ul.appendChild(li);
  }
  detachedTree = ul;
}

document.getElementById('create').addEventListener('click', create);

Нажатие кнопки, указанной в коде, создает узел ul с десятью дочерними элементами li . На эти узлы ссылается код, но они не существуют в дереве DOM, поэтому они отключены.

Снимки кучи — один из способов идентификации отдельных узлов. Как следует из названия, снимки кучи показывают, как память распределяется между объектами JS вашей страницы и узлами DOM в момент создания снимка.

Чтобы создать снимок, откройте DevTools и перейдите на панель «Память» , выберите переключатель «Снимок кучи» , а затем нажмите кнопку «Сделать снимок» .

Выбран переключатель «Сделать снимок кучи».

Обработка и загрузка снимка может занять некоторое время. По завершении выберите его на левой панели (с названием «Снимки кучи» ).

Введите Detached в поле ввода «Фильтр классов», чтобы найти отдельные деревья DOM.

Фильтрация отдельных узлов.

Расширьте караты, чтобы исследовать отдельно стоящее дерево.

Исследование отдельно стоящего дерева.

Щелкните узел, чтобы исследовать его дальше. На панели «Объекты» вы можете увидеть дополнительную информацию о коде, который ссылается на него. Например, на следующем снимке экрана вы можете видеть, что переменная detachedTree ссылается на узел. Чтобы устранить эту конкретную утечку памяти, вам следует изучить код, использующий detachedTree , и убедиться, что он удаляет ссылку на узел, когда он больше не нужен.

Исследование оторвавшегося узла.

Выявление утечек памяти в куче JS с помощью временных шкал распределения

Временная шкала распределения — это еще один инструмент, который может помочь вам отслеживать утечки памяти в куче JS.

Чтобы продемонстрировать временную шкалу распределения, рассмотрим следующий код:

var x = [];

function grow() {
  x.push(new Array(1000000).join('x'));
}

document.getElementById('grow').addEventListener('click', grow);

Каждый раз при нажатии кнопки, указанной в коде, в массив x добавляется строка из миллиона символов.

Чтобы записать временную шкалу распределения , откройте DevTools, перейдите на панель « Память », выберите переключатель «Распределения на временной шкале» , нажмите кнопку «Запись» , выполните действие, которое, как вы подозреваете, вызывает утечку памяти, а затем нажмите кнопку «Запись». Кнопка остановки записи, когда вы закончите.

Во время записи обратите внимание, появляются ли какие-либо синие полосы на временной шкале распределения , как на следующем снимке экрана.

Новые распределения в графике производительности.

Эти синие полосы обозначают новые распределения памяти. Эти новые распределения памяти являются вашими кандидатами на утечку памяти. Вы можете увеличить масштаб панели, чтобы отфильтровать панель «Конструктор» , чтобы отображались только объекты, выделенные в течение указанного периода времени.

Увеличенная временная шкала распределения.

Разверните объект и щелкните его значение, чтобы просмотреть более подробную информацию о нем на панели «Объект» . Например, на снимке экрана ниже, просматривая сведения о недавно выделенном объекте, вы сможете увидеть, что он был назначен переменной x в области Window .

Сведения об объекте массива строк.

Исследование распределения памяти по функциям

Используйте тип профиля выборки распределения на панели «Память» , чтобы просмотреть распределение памяти с помощью функции JavaScript.

Профилировщик выборки распределения на панели «Память».

  1. Выберите переключатель Выборка распределения . Если на странице есть рабочий процесс, вы можете выбрать его в качестве цели профилирования в окне «Выбор экземпляра виртуальной машины JavaScript» .
  2. Нажмите кнопку «Пуск» .
  3. Выполните действия на странице, которую хотите исследовать.
  4. Нажмите кнопку «Стоп» , когда вы закончите все свои действия.

DevTools показывает распределение памяти по функциям. По умолчанию используется представление Heavy (Bottom Up) , в котором вверху отображаются функции, которым выделено больше всего памяти.

Страница результатов профиля распределения.

Определить объекты, сохраняемые по ссылке JS

В профиле «Отсоединенные элементы» показаны отдельные элементы, которые сохраняются, поскольку на них ссылается код JavaScript.

Запишите профиль отдельных элементов, чтобы просмотреть точные узлы HTML и их количество.

Пример профиля отдельных элементов.

Отмечайте частые вывозы мусора

Если ваша страница часто приостанавливается, возможно, у вас проблемы со сборкой мусора.

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

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