Запись снимков кучи

Меггин Кирни
Meggin Kearney
София Емельянова
Sofia Emelianova

Узнайте, как записывать снимки кучи с помощью «Память» > «Профили» > «Снимок кучи» и находить утечки памяти.

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

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

Чтобы сделать снимок кучи:

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

Выбранный тип профилирования и экземпляр виртуальной машины JavaScript.

Когда панель «Память» загружает и анализирует снимок, под заголовком снимка в разделе HEAP SNAPSHOTS отображается общий размер доступных объектов JavaScript .

Общий размер достижимых объектов.

Снимки показывают только те объекты из графа памяти, которые доступны из глобального объекта. Создание снимка всегда начинается со сбора мусора.

Куча снимков разбросанных объектов Item.

Очистить снимки

Чтобы удалить все снимки, нажмите . Очистить все профили :

Очистите все профили.

Просмотр снимков

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

Вид Содержание Цель
Краткое содержание Объекты сгруппированы по именам конструкторов. Используйте его для поиска объектов и использования ими памяти в зависимости от типа. Полезно для отслеживания утечек DOM .
Сравнение Различия между двумя снимками. Используйте его для сравнения двух (или более) снимков до и после операции. Подтвердите наличие и причину утечки памяти, проверив разницу в освобожденной памяти и счетчике ссылок.
Сдерживание Содержимое кучи Обеспечивает лучшее представление о структуре объекта и помогает анализировать объекты, на которые имеются ссылки в глобальном пространстве имен (окне), чтобы найти, что их держит. Используйте его для анализа замыканий и погружения в объекты на низком уровне.
Статистика Круговая диаграмма распределения памяти Посмотрите реальные размеры частей памяти, выделенных для кода, строк, массивов JS, типизированных массивов и системных объектов.

Представление «Сводка», выбранное в раскрывающемся меню вверху.

Сводный вид

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

Представление «Сводка» с расширенным конструктором.

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

Числа рядом с именами конструкторов указывают общее количество объектов, созданных с помощью конструктора. В представлении «Сводка» также отображаются следующие столбцы:

  • Расстояние показывает расстояние до корня, используя кратчайший простой путь узлов.
  • Мелкий размер показывает сумму мелких размеров всех объектов, созданных определенным конструктором. Небольшой размер — это размер памяти, занимаемой самим объектом. Обычно массивы и строки имеют более мелкие размеры. См. также Размеры объектов .
  • Сохраненный размер показывает максимальный сохраняемый размер среди одного и того же набора объектов. Сохраняемый размер — это размер памяти, который вы можете освободить, удалив объект и сделав его зависимые объекты недоступными. См. также Размеры объектов .

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

Конструктор фильтров

Представление сводки позволяет фильтровать конструкторы на основе распространенных случаев неэффективного использования памяти.

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

  • Все объекты : все объекты, захваченные текущим снимком. Установлено по умолчанию.
  • Объекты, выделенные до снимка 1 : объекты, которые были созданы и оставались в памяти до того, как был сделан первый снимок.
  • Объекты, распределенные между снимками 1 и снимками 2 : просмотр разницы в объектах между самым последним снимком и предыдущим снимком. Каждый новый снимок добавляет приращение этого фильтра в раскрывающийся список.
  • Дублированные строки : строковые значения, которые были сохранены в памяти несколько раз.
  • Объекты, сохраняемые отдельными узлами : объекты, которые сохраняются, поскольку на них ссылается отдельный узел DOM.
  • Объекты, сохраняемые консолью DevTools : объекты, хранящиеся в памяти, поскольку они оценивались или взаимодействовали с консолью DevTools.

Специальные записи в сводке

Помимо группировки по конструкторам, представление «Сводка» также группирует объекты по:

  • Встроенные функции, такие как Array или Object .
  • HTML-элементы, сгруппированные по тегам, например, <div> , <a> , <img> и другие.
  • Функции, которые вы определили в своем коде.
  • Специальные категории, не основанные на конструкторах.

Записи конструктора.

(array)

В эту категорию входят различные внутренние объекты, подобные массивам, которые не соответствуют напрямую объектам, видимым в JavaScript.

Например, содержимое объектов JavaScript Array хранится во вторичном внутреннем объекте с именем (object elements)[] , чтобы упростить изменение размера. Аналогично, именованные свойства в объектах JavaScript часто хранятся во вторичных внутренних объектах с именем (object properties)[] , которые также перечислены в категории (array) .

(compiled code)

Эта категория включает внутренние данные, необходимые V8 для выполнения функций, определенных JavaScript или WebAssembly. Каждую функцию можно представить разными способами: от маленького и медленного до большого и быстрого.

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

(concatenated string)

Когда V8 объединяет две строки, например, с помощью оператора JavaScript + , он может выбрать внутреннее представление результата как «объединенную строку», также известную как структура данных Rope .

Вместо того, чтобы копировать все символы двух исходных строк в новую строку, V8 выделяет небольшой объект с внутренними полями, называемыми first и second , которые указывают на две исходные строки. Это позволяет V8 экономить время и память. С точки зрения кода JavaScript это обычные строки, и они ведут себя как любая другая строка.

InternalNode

Эта категория представляет объекты, размещенные за пределами V8, например объекты C++, определенные Blink .

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

  1. Откройте DevTools и включите «Настройки» > «Эксперименты» > «Показать опцию», чтобы отображать внутренние компоненты в снимках кучи .
  2. Откройте панель «Память» , выберите Heap snapshot и включите Expose Internals (включая дополнительные сведения, специфичные для реализации) .
  3. Воспроизведите проблему, из-за которой InternalNode сохранял много памяти.
  4. Сделайте снимок кучи. В этом снимке объекты имеют имена классов C++ вместо InternalNode .
(object shape)

Как описано в разделе «Быстрые свойства» в V8 , V8 отслеживает скрытые классы (или формы ), чтобы можно было эффективно представить несколько объектов с одинаковыми свойствами в одном и том же порядке. Эта категория содержит скрытые классы, называемые system / Map (не связанные с JavaScript Map ), и связанные данные.

(sliced string)

Когда V8 необходимо взять подстроку, например, когда код JavaScript вызывает String.prototype.substring() , V8 может выбрать выделение нарезанного строкового объекта вместо копирования всех соответствующих символов из исходной строки. Этот новый объект содержит указатель на исходную строку и описывает, какой диапазон символов исходной строки следует использовать.

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

system / Context

Внутренние объекты типа system / Context содержат локальные переменные из замыкания — область JavaScript, к которой может обращаться вложенная функция.

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

(system)

Эта категория содержит различные внутренние объекты, которые (пока) не были классифицированы более значимым образом.

Сравнительный вид

Представление «Сравнение» позволяет находить просочившиеся объекты путем сравнения нескольких снимков друг с другом. Например, выполнение действия и его отмена, например открытие и закрытие документа, не должны оставлять после себя лишние объекты.

Чтобы убедиться, что определенная операция не приводит к утечкам:

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

В представлении «Сравнение» отображается разница между двумя снимками. При раскрытии общей записи отображаются добавленные и удаленные экземпляры объектов:

По сравнению со снимком 1.

Вид на сдерживание

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

Представление предоставляет несколько точек входа:

  • Объекты DOMWindow . Глобальные объекты для кода JavaScript.
  • Корни ГК . Корни GC, используемые сборщиком мусора виртуальной машины. Корни GC могут состоять из встроенных карт объектов, таблиц символов, стеков потоков виртуальной машины, кэшей компиляции, областей дескрипторов и глобальных дескрипторов.
  • Родные объекты . Объекты браузера «вставляются» внутрь виртуальной машины JavaScript, чтобы обеспечить автоматизацию, например узлы DOM и правила CSS.

Взгляд на сдерживание.

Раздел «Рейтингеры»

В разделе «Сохранители» в нижней части панели «Память» отображаются объекты, указывающие на объект, выбранный в представлении. На панели «Память» раздел «Сохранители» обновляется при выборе других объектов в любом из представлений, кроме «Статистика» .

Раздел «Ретейнеры».

В этом примере выбранная строка сохраняется свойством x экземпляра Item .

Игнорировать гонорары

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

Параметр «Игнорировать этот фиксатор» в раскрывающемся меню.

Чтобы скрыть фиксатор, щелкните правой кнопкой мыши и выберите «Игнорировать этот фиксатор» . Игнорируемые фиксаторы помечаются как ignored в столбце «Расстояние» . Чтобы перестать игнорировать все удержания, нажмите Восстановить игнорируемые удержания на панели действий вверху.

Найдите конкретный объект

Чтобы найти объект в собранной куче, вы можете выполнить поиск с помощью Ctrl + F и ввести идентификатор объекта.

Назовите функции, чтобы различать замыкания.

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

Например, следующий код не использует именованные функции:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function() { // this is NOT a named function
    return largeStr;
  };

  return lC;
}

Хотя этот пример делает:

function createLargeClosure() {
  var largeStr = new Array(1000000).join('x');

  var lC = function lC() { // this IS a named function
    return largeStr;
  };

  return lC;
}

Именованная функция в замыкании.

Обнаружение утечек DOM

Профилировщик кучи имеет возможность отражать двунаправленные зависимости между собственными для браузера объектами (узлами DOM и правилами CSS) и объектами JavaScript. Это помогает обнаружить невидимые в противном случае утечки, возникающие из-за плавающих вокруг забытых отдельных поддеревьев DOM.

Утечки DOM могут быть больше, чем вы думаете. Рассмотрим следующий пример. Когда собирают #tree мусор?

  var select = document.querySelector;
  var treeRef = select("#tree");
  var leafRef = select("#leaf");
  var body = select("body");

  body.removeChild(treeRef);

  //#tree can't be GC yet due to treeRef
  treeRef = null;

  //#tree can't be GC yet due to indirect
  //reference from leafRef

  leafRef = null;
  //#NOW #tree can be garbage collected

#leaf поддерживает ссылку на своего родителя ( parentNode ) и рекурсивно вплоть до #tree , поэтому только когда leafRef обнуляется, все дерево под #tree становится кандидатом на GC.

Поддеревья DOM