Поделиться через


Запись моментальных снимков кучи с помощью средства "Память" (тип профилирования "Куча snapshot")

Используйте профилировщик кучи в средстве "Память ", чтобы сделать следующее:

  • Запись моментальных снимков кучи JavaScript (кучи JS).
  • Анализ графов памяти.
  • Сравнение моментальных снимков.
  • Поиск утечек памяти.

Профилировщик кучи DevTools показывает распределение памяти, используемое объектами JavaScript и связанными узлами DOM на отрисовываемой веб-странице.

Возьмите snapshot

  1. Откройте веб-страницу, которую вы хотите проанализировать. Например, откройте демонстрационную страницу "Рассеянные объекты " в новом окне или вкладке.

  2. Чтобы открыть Средства разработки, щелкните веб-страницу правой кнопкой мыши и выберите пункт Проверить. Или нажмите клавиши CTRL+SHIFT+I (Windows, Linux) или COMMAND+OPTION+I (macOS). Откроется devTools.

  3. В Средствах разработки на панели действий выберите вкладку Память . Если эта вкладка не отображается, нажмите кнопку Дополнительные инструменты (значок ") .

  4. В разделе Выбор типа профилирования нажмите кнопку Snapshot кучи.

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

  6. Нажмите кнопку Принять snapshot:

Выбран инструмент

После загрузки snapshot новой кучи в средства разработки и анализа отображается snapshot, а на боковой панели Профили в разделе Моментальные снимки кучи появится новая запись:

Общий размер доступных объектов

Число под новым элементом боковой панели показывает общий размер доступных объектов JavaScript. Дополнительные сведения о размерах объектов в куче snapshot см. в статье Размеры и расстояния объектов в терминологии памяти.

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

Еще один snapshot

Чтобы выполнить еще один snapshot, когда он уже отображается в средстве "Память", на боковой панели щелкните Профили над существующим snapshot:

Кнопка Профили для другого snapshot

Очистка моментальных снимков

Чтобы очистить все моментальные снимки из средства "Память ", щелкните значок Очистить все профили (значок очистки):

Удаление моментальных снимков

Просмотр моментальных снимков

Моментальные снимки кучи можно просматривать различными способами в средстве "Память ". Каждый способ просмотра кучи snapshot в пользовательском интерфейсе соответствует другой задаче:

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

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

Селектор переключения представлений

Элементы, пропущенные из моментальных снимков кучи

Свойства, реализованные с помощью средств получения, выполняющих машинный код, не записываются в кучу snapshot, так как такие свойства не хранятся в куче JavaScript.

Нестроковые значения, такие как числа, не записываются.

Сводное представление

В представлении Сводка в средстве "Память " перечислены следующие списки:

  • Группы конструкторов объектов.
  • Специальные имена категорий, такие как (массив),(скомпилированный код) или список свойств, таких как {foo, bar, baz}.

Изначально в представлении Сводка открывается snapshot куча, в которой отображается список конструкторов:

Сводное представление

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

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

Столбец Описание
Distance (Расстояние) Отображает расстояние до корня, используя самый короткий простой путь узлов. См. раздел Расстояние в терминологии памяти.
Неглубокий размер Отображает сумму мелких размеров всех объектов, созданных определенной функцией конструктора. Мелкий размер — это размер кучи JavaScript, непосредственно удерживаемой объектом. Мелкий размер объекта обычно невелик, так как объект JavaScript часто хранит только описание объекта, а не его значения в непосредственной памяти объекта. Большинство объектов JavaScript хранят свои значения в резервном хранилище , которое находится в другом месте в куче JavaScript, и предоставляют только небольшой объект-оболочку в той части кучи JavaScript, которая непосредственно принадлежит объекту . См. раздел Мелкий размер в терминологии памяти.
Сохраненный размер Отображает максимальный сохраненный размер для одного и того же набора объектов. Размер памяти, которую можно освободить после удаления объекта (а зависимые объекты становятся недоступными), называется сохраненным размером. См. раздел Сохраненный размер в терминологии памяти.

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

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

В представлении Сводка в средстве "Память " перечислены группы конструкторов объектов:

Группы конструкторов

Группы конструкторов в представлении Сводка могут быть встроенными функциями, такими как Array или Object, или функциями, определенными в вашем собственном коде.

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

Специальные имена категорий

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

Имя категории Описание
(массив) Различные внутренние объекты, подобные массиву, которые напрямую не соответствуют объектам, видимым из JavaScript, например содержимому массивов JavaScript или именованным свойствам объектов JavaScript.
(скомпилированный код) Внутренние данные, необходимые версии 8 (обработчик JavaScript Microsoft Edge) для выполнения функций, определенных JavaScript или WebAssembly. Версия 8 автоматически управляет использованием памяти в этой категории: если функция выполняется много раз, V8 использует больше памяти для этой функции, чтобы функция выполнялось быстрее. Если функция некоторое время не выполнялась, версия 8 может удалить внутренние данные для этой функции.
(объединенная строка) Если две строки объединяются вместе, например при использовании оператора JavaScript + , V8 может представить результат внутри в виде сцепленной строки. Вместо копирования всех символов двух строк в новую строку V8 создает небольшой объект, указывающий на две строки.
(фигура объекта) Сведения об объектах, такие как количество имеющихся у них свойств и ссылка на их прототипы, которые V8 поддерживает внутренне при создании и обновлении объектов. Это позволяет V8 эффективно представлять объекты с теми же свойствами.
(срезанная строка) При создании подстроки, например при использовании метода JavaScript substring , V8 может создать срез строковый объект, а не копировать все соответствующие символы из исходной строки. Этот новый объект содержит указатель на исходную строку и описывает диапазон символов из исходной строки для использования.
(система) Различные внутренние объекты, которые еще не были классифицированы более значимым образом.
{foo, bar, baz} Обычные объекты JavaScript, классифицированные по интерфейсу (списку свойств), в фигурных скобках. Обычные объекты JavaScript не перечислены в категории с именем Object, но вместо этого представлены именами и категориями, основанными на свойствах, содержащихся в объекте, таких как {foo, bar, baz}.
InternalNode Объекты, выделенные за пределами версии 8, например объекты C++, определенные Blink, обработчик отрисовки Microsoft Edge.
system / Context Локальные переменные из область JavaScript, доступ к которым может получить какая-то вложенная функция. Каждый экземпляр функции содержит внутренний указатель на контекст, в котором он выполняется, чтобы он смог получить доступ к этим переменным.

Представление сравнения

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

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

  1. Перед выполнением операции сделайте кучу snapshot.

  2. Выполните операцию. То есть взаимодействовать со страницей каким-то образом, что может привести к утечке.

  3. Выполните обратную операцию. То есть сделайте обратное взаимодействие и повторите его несколько раз.

  4. Возьмите вторую кучу snapshot.

  5. Во второй куче snapshot измените представление на Сравнение, сравнив его с моментальным снимком 1.

В представлении Сравнения отображается разница между двумя моментальными снимками:

Представление сравнения

При расширении конструктора в списке отображаются добавленные и удаленные экземпляры объектов.

Представление "Автономное"

Представление Containment позволяет заглянуть внутрь закрытия функций, наблюдать за внутренними объектами виртуальных машин, составляющими объекты JavaScript, и понять, сколько памяти приложение использует на очень низком уровне:

Представление

В представлении Containment отображаются следующие типы объектов:

Точки входа в представление "Автономное" Описание
Объекты DOMWindow Глобальные объекты для кода JavaScript.
Корни мусора Корневые данные сборки мусора, используемые сборщиком мусора виртуальной машины JavaScript. Корневые элементы сборки мусора состоят из встроенных карт объектов, таблиц символов, стеков потоков виртуальных машин, кэшей компиляции, областей дескрипторов и глобальных дескрипторов.
Собственные объекты Объекты, созданные браузером, такие как узлы DOM и правила CSS, которые отображаются на виртуальной машине JavaScript для обеспечения автоматизации.

Раздел Хранимые средства

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

На следующем снимке экрана в представлении Сводка был выбран строковый объект, а в разделе Хранимые данные показано, что строка сохраняется свойством x экземпляра класса, найденного Item в example-03.js файле:

Раздел Хранимые средства

Скрытие циклов

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

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

В раскрывающемся меню Фильтр ребер в разделе Хранители выбрано

Скрытие внутренних узлов

Внутренние узлы — это объекты, относящиеся к версии 8 (подсистема JavaScript в Microsoft Edge).

Чтобы скрыть внутренние узлы в разделе Средства хранения , в раскрывающемся меню Фильтр ребер выберите Скрыть внутренние.

Фильтрация моментальных снимков кучи по типам узлов

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

Чтобы сосредоточиться только на определенных типах узлов, используйте фильтр Типы узлов в правом верхнем углу. Например, чтобы просмотреть только массивы и строковые объекты в куче snapshot:

  1. Чтобы открыть фильтр Типы узлов , щелкните По умолчанию в правом верхнем углу.

  2. Выберите записи Массив и Строка .

    В snapshot кучи отображаются только объекты массива и строки:

    Типы узлов в куче snapshot в средстве

Поиск определенного объекта

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

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

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

Сведения об отсоединяемых элементах см. также в разделе Поиск утечек памяти дерева DOM ("Куча snapshot" тип > профилирования Detached) ниже.

Рассмотрим следующее дерево DOM:

Поддеревры DOM

В следующем примере кода создаются переменные treeRef JavaScript и leafRef, которые ссылались на два узла DOM в дереве:

// Get a reference to the #tree element.
const treeRef = document.querySelector("#tree");

// Get a reference to the #leaf element,
// which is a descendant of the #tree element.
const leafRef = document.querySelector("#leaf");

В следующем примере <div id="tree"> кода элемент удаляется из дерева DOM:

// Remove the #tree element from the DOM.
document.body.removeChild(treeRef);

Элемент <div id="tree"> не может быть собран при сборке мусора, так как переменная treeRef JavaScript все еще существует. Переменная treeRef напрямую ссылается на <div id="tree"> элемент . В следующем примере treeRef кода переменная получает значение NULL:

// Remove the treeRef variable.
treeRef = null;

Элемент <div id="tree"> по-прежнему не может быть собран при сборке мусора, так как переменная leafRef JavaScript по-прежнему существует. Свойство leafRef.parentNode ссылается на <div id="tree"> элемент . В следующем примере leafRef кода переменная получает значение NULL:

// Remove the leafRef variable.
leafRef = null;

На этом этапе <div id="tree"> элемент может быть собран с помощью мусора. Оба treeRef элемента leafRef и должны сначала быть обнуляемы, чтобы все дерево DOM в элементе <div id="tree"> было собрано мусором.

Демонстрационная веб-страница. Пример 6. Утечка узлов DOM

Чтобы понять, где могут протекать узлы DOM и как обнаружить такую утечку, откройте пример веб-страницы Пример 6. Утечка узлов DOM в новом окне или вкладке.

Демонстрационная веб-страница. Пример 9. Утечки DOM больше, чем ожидалось

Чтобы узнать, почему утечка DOM может быть больше, чем ожидалось, откройте пример веб-страницы Пример 9. Утечки DOM больше, чем ожидалось , в новом окне или вкладке.

Анализ влияния замыкания на память

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

  1. Откройте демонстрационную веб-страницу Eval is evil в новом окне или вкладке.

  2. Запишите snapshot кучи.

  3. На отображаемой веб-странице нажмите кнопку Закрытия с вывеской .

  4. Запишите вторую кучу snapshot.

    На боковой панели число ниже второго snapshot должно быть больше, чем число ниже первого snapshot. Это означает, что веб-страница использует больше памяти после нажатия кнопки Закрытия с eval .

  5. Во второй куче snapshot измените представление на Сравнение, а затем сравните вторую кучу snapshot с первой snapshot кучи.

    В представлении Сравнение показано, что новые строки были созданы во второй куче snapshot:

    Представление сравнения, показывающее, что во второй snapshot были созданы новые строки

  6. В представлении Сравнение разверните конструктор (строка).

  7. Щелкните первую (строку) запись.

    Раздел Хранимые данные обновлен и показывает, что largeStr переменная сохраняет строку, выбранную в представлении сравнения .

    Запись largeStr автоматически разворачивается и показывает, что переменная сохраняется функцией eC , которая является закрытием, где определена переменная:

    Раздел Хранители, показывающий, что строка сохраняется функцией eC

Совет. Назовите функции для различения закрытий в snapshot

Чтобы легко различать закрытия JavaScript в куче snapshot, присвойте имена функций.

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

function createLargeClosure() {
    const largeStr = 'x'.repeat(1000000).toLowerCase();

    // This function is unnamed.
    const lC = function() {
        return largeStr;
    };

    return lC;
}

В следующем примере функция называется, что упрощает распознавание замыкания в куче snapshot:

function createLargeClosure() {
    const largeStr = 'x'.repeat(1000000).toLowerCase();

    // This function is named.
    const lC = function lC() {
        return largeStr;
    };

    return lC;
}

Сохранение и экспорт строк из кучи snapshot в JSON

При выполнении кучи snapshot в средстве "Память" можно экспортировать все строковые объекты из snapshot в JSON-файл. В средстве Память в разделе Конструктор нажмите кнопку Сохранить все в файл рядом с записью (string) :

Сохранение всех строк из кучи snapshot в JSON

Средство "Память" экспортирует JSON-файл, содержащий все строковые объекты из кучи snapshot:

Строки из кучи snapshot в JSON-файле

Поиск утечек памяти дерева DOM (тип > профилирования "Куча snapshot" Отсоединен)

Один из способов найти и отобразить все отсоединенные элементы на веб-странице — использовать snapshot типа профилирования средства "Память", а затем в текстовом поле Фильтр по классам введите Отсоединенные, как показано ниже. См. также статью Средства для исследования отсоединяемых элементов в статье Устранение проблем с памятью.

Следующий код создает отсоединяемые узлы 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);

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

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

Использование типа профилирования "Куча snapshot" для поиска отсоединяемых элементов

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

  1. Откройте веб-страницу, например демонстрационную веб-страницу Отсоединенные элементы, в новом окне или вкладке.

  2. Щелкните веб-страницу правой кнопкой мыши и выберите Пункт Проверить. Или нажмите клавиши CTRL+SHIFT+I (Windows, Linux) или COMMAND+OPTION+I (macOS).

    Откроется devTools.

  3. В Средствах разработки на панели действий выберите значокПамять (память).

    Если эта вкладка не отображается, нажмите кнопку Дополнительные инструменты (другие инструменты) и выберите Память. Откроется средство "Память ":

    Открытие средства

    Если кнопка "Куча snapshot" не отображается, так как профиль уже отображается, в левом верхнем углу щелкните Профили (значок Профили).

    На этом этапе не нужно нажимать кнопку Куча snapshot, так как веб-страница еще не создает отсоединенные элементы.

    Создайте сообщения, которые будут храниться экземпляром JavaScript класса Room:

  4. На демонстрационной веб-странице нажмите кнопку Быстрый трафик .

    Демонстрационная веб-страница начинает создавать сообщения и отображать их на веб-странице:

    Создание сообщений на демонстрационной веб-странице

  5. После отображения некоторых сообщений нажмите кнопку Остановить на демонстрационной веб-странице.

    Каждое сообщение является элементом <div class="message"> , на который ссылается экземпляр Room Класса Room 1. В дереве DOM веб-страницы нет отсоединяемых элементов, так как все элементы сообщения присоединяются к настоящему экземпляру класса Room 1.

    Измените на другой экземпляр класса Room, чтобы элементы стали отсоединяться:

  6. На демонстрационной веб-странице нажмите кнопку Комната 2 , которая соответствует другому экземпляру Room класса .

    На веб-странице сообщения исчезают:

    Начальный вид комнаты 2

    Сообщения, созданные для экземпляра Room 1 класса Room (<div class="message"> элементы), больше не присоединяются к DOM, но на них по-прежнему ссылается экземпляр Room 1 класса Room . Они являются отсоединяемыми элементами, которые могут привести к утечке памяти, если они не будут использоваться снова веб-страницей.

    Получите список отсоединяемых элементов:

  7. В средствах разработки в средстве "Память " щелкните значок Сбор мусора (значок :

    Получение кучи snapshot

    Браузер запускает сборку мусора, удаляя все узлы, на которые больше не ссылается объект JavaScript.

  8. В средстве "Память" нажмите кнопку Snapshot кучи.

  9. Нажмите кнопку Принять snapshot в нижней части средства "Память".

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

  10. В текстовом поле Фильтровать по классам введите detached:

    Фильтрация по отсоединяющимся узлам и расширение отсоединяемого узла

    Отсоединенные элементы DOM, которые не могут быть собраны в мусор.

    Определите код JavaScript, который ссылается на конкретный отсоединяемый элемент:

  11. В snapshot кучи разверните отсоединяемый объект, например Отсоединяемый <div>, а затем выберите узел Отсоединяемый <div class="message".>

    Сведения отображаются в области Средства хранения в нижней части средства "Память ".

  12. В области Средства хранения щелкните ссылку room.js:13 для отключенного элемента в помещении в разделе Массив. Откроется инструмент Источники с прокрученным room.jsдо строки 13:

    Исходный код JavaScript в средстве

  13. Чтобы проверить возможную утечку памяти, изучите код, использующий unmounted массив, и убедитесь, что ссылка на узел удаляется, когда она больше не нужна.

  14. Чтобы вернуться к средству "Память ", в адресной строке выберите средство "Память ".

Дополнительные способы просмотра отсоединяемых элементов см. в статье Средства для исследования отсоединяемых элементов в статье Устранение проблем с памятью.

См. также

Внешний:

Примечание.

Части этой страницы являются изменениями, основанными на работе, созданной и совместно используемой Google и используемой в соответствии с условиями, описанными в международной лицензии Creative Commons Attribution 4.0. Исходная страница находится здесь и автор Меггин Кирни.

Creative Commons License Эта работа лицензируется по международной лицензии Creative Commons Attribution 4.0.