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

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

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

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

Обзор

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

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

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

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

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

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

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

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

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

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

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

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

    Включение JS-памяти

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

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

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

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

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

Чтобы продемонстрировать записи в памяти Performance, рассмотрите приведенный ниже код:

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 и перейдите на панель «Память» , выберите переключатель «Снимок кучи» , а затем нажмите кнопку «Сделать снимок» .

сделать снимок кучи

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

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

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

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

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

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

Нажмите на желтый узел, чтобы исследовать его дальше. На панели «Объекты» вы можете увидеть дополнительную информацию о коде, который ссылается на него. Например, на скриншоте ниже вы можете видеть, что переменная 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. Выберите переключатель «Выборка распределения» . Если на странице есть работник, вы можете выбрать его в качестве цели профилирования, используя раскрывающееся меню рядом с кнопкой «Пуск» .
  2. Нажмите кнопку «Пуск» .
  3. Выполните действия на странице, которую хотите исследовать.
  4. Нажмите кнопку «Стоп» , когда вы закончите все свои действия.

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

Профиль распределения

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

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

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

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