Модуль управления окном памяти

Обновлено: 30.06.2024

Используйте это окно для получения и отображения данных о времени выполнения и использовании памяти ВП. Если вы открываете окно Profile Performance and Memory из ВП, не являющегося частью проекта, в окне профилируются все открытые ВП, не принадлежащие к проекту. Если вы открываете окно Profile Performance and Memory из проекта или ВП, который является частью проекта, окно профилирует ВП во всех целевых объектах, связанных с проектом. Вы можете одновременно профилировать ВП только в одном проекте.

Это окно не поддерживает удаленную отладку. Если вы откроете окно Profile Performance and Memory во время удаленной отладки, окно профилирует VI только на локальном компьютере.

Примечание. Вы можете использовать Desktop Execution Trace Toolkit для мониторинга анализа кода, утечек памяти и других аспектов программирования LabVIEW.
Примечание. Вы можете использовать LabVIEW VI Analyzer Toolkit для интерактивной или программной проверки ВП на предмет производительности, эффективности и других аспектов программирования LabVIEW.

Это окно включает следующие компоненты:

  • Avg Bytes — среднее количество байтов, используемых пространством данных VI за один запуск.
  • Мин. байтов — минимальное количество байтов, используемых пространством данных ВП для отдельного запуска.
  • Max Bytes — максимальное количество байтов, используемых пространством данных VI для отдельного запуска.
  • Среднее количество блоков — среднее количество блоков, используемых пространством данных ВП за один запуск.
  • Минимальное количество блоков — минимальное количество блоков, используемых пространством данных ВП для отдельного запуска.
  • Max Blocks — максимальное количество блоков, используемых пространством данных VI для отдельного запуска.

  • Время ВП — общее время, затраченное на выполнение кода ВП и отображение его данных, а также время, затраченное пользователем на взаимодействие с любыми элементами управления на лицевой панели. Для глобальных ВП это время равно общему времени, затраченному на копирование данных во все его элементы управления или из них. Дважды щелкните имя глобального ВП, чтобы просмотреть информацию о времени для отдельных элементов управления.
  • Время ВПП — общее время, затраченное всеми ВПП ВП. Это сумма Времени ВП для всех вызываемых ВП, а также их вызываемых и т. д.
  • Общее время — сумма времени ВП и времени ВПП, подсчет общего времени.

В WinDbg вы можете просматривать и редактировать память, вводя команды или используя окно памяти.

Командное окно отладчика

Вы можете просмотреть память, введя одну из команд Display Memory в окне команд отладчика. Вы можете отредактировать память, введя одну из команд Enter Values ​​в окне команд отладчика. Дополнительные сведения см. в разделах Доступ к памяти по виртуальному адресу и Доступ к памяти по физическому адресу.

Открытие окна памяти

снимок экрана кнопка памяти». /><br /></p>
<p>Чтобы открыть окно «Память», выберите «Память» в меню «Вид». (Вы также можете нажать ALT+5 или выбрать кнопку Память () на панели инструментов. ALT+SHIFT+5 закрывает активное окно Память.)</p>
<p>На следующем снимке экрана показан пример окна памяти.</p>
<p><img class=

Использование окна памяти

В окне "Память" данные отображаются в нескольких столбцах. Столбец в левой части окна показывает начальный адрес каждой строки. Остальные столбцы отображают запрошенную информацию слева направо. Если выбрать «Байты» в меню «Формат отображения», символы ASCII, соответствующие этим байтам, будут отображаться в правой части окна.

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

В окне "Память" вы можете сделать следующее:

Чтобы записать в память, выберите в окне "Память" и введите новые данные. Вы можете редактировать только шестнадцатеричные данные — вы не можете напрямую редактировать символы ASCII и Unicode. Изменения вступают в силу, как только вы вводите новую информацию.

Чтобы просмотреть другие разделы памяти, используйте кнопки «Назад» и «Далее» на панели инструментов окна «Память» или нажмите клавиши PAGE UP или PAGE DOWN. Эти кнопки и клавиши отображают непосредственно предшествующие или следующие разделы памяти. Если вы запросите недопустимую страницу, появится сообщение об ошибке.

Для навигации в окне используйте клавиши СТРЕЛКА ВПРАВО, СТРЕЛКА ВЛЕВО, СТРЕЛКА ВВЕРХ и СТРЕЛКА ВНИЗ. Если вы используете эти клавиши для перехода со страницы, отображается новая страница.Прежде чем использовать эти клавиши, следует изменить размер окна Память, чтобы в нем не было полос прокрутки. Этот размер позволяет отличить фактический край страницы от края окна.

Чтобы изменить просматриваемую ячейку памяти, введите новый адрес в поле адреса в верхней части окна Память. Обратите внимание, что окно Память обновляет свое отображение, когда вы вводите адрес, поэтому вы можете получать сообщения об ошибках до того, как завершите ввод адреса. Примечание. Адрес, который вы вводите в поле, интерпретируется в текущей системе счисления. Если текущая система счисления не 16, вы должны добавить префикс 0x к шестнадцатеричному адресу. Чтобы изменить систему счисления по умолчанию, используйте команду n (задать числовую базу) в окне команд отладчика. Отображение в самом окне памяти не зависит от текущей системы счисления.

Чтобы изменить тип данных, который окно использует для отображения памяти, используйте меню Формат отображения на панели инструментов окна Память. Поддерживаемые типы данных включают короткие слова, двойные слова и четверные слова; короткие, длинные и четверные целые числа и целые числа без знака; 10-байтовые, 16-байтовые, 32-байтовые и 64-байтовые действительные числа; символы ASCII; символы Юникода; и шестнадцатеричные байты. Отображение шестнадцатеричных байтов также включает символы ASCII.

снимок экрана кнопка, которая отображает контекстное меню панели инструментов окна памяти». /><br /></p>
<p>В окне «Память» есть панель инструментов с двумя кнопками, меню и полем, а также контекстное меню с дополнительными командами. Чтобы получить доступ к меню, выберите и удерживайте (или щелкните правой кнопкой мыши) строку заголовка или выберите значок в правом верхнем углу окна (). Панель инструментов и контекстное меню содержат следующие пункты:</p>
<p>(только панель инструментов) Поле адреса позволяет указать новый адрес или смещение. Точное значение этого поля зависит от типа просматриваемой памяти. Например, если вы просматриваете виртуальную память, это поле позволяет указать новый виртуальный адрес или смещение.</p>
<p>(Только панель инструментов) Формат отображения позволяет выбрать новый формат отображения.</p>
<p>(Панель инструментов и меню) Предыдущая (на панели инструментов) и Предыдущая страница (в контекстном меню) вызывают отображение предыдущего раздела памяти.</p>
<p>(Панель инструментов и меню) «Далее» (на панели инструментов) и «Следующая страница» (в контекстном меню) вызывают отображение следующего раздела памяти.</p>
<p>(Только меню) Панель инструментов включает и выключает панель инструментов.</p>
<p>(Только меню) Автоподбор столбцов гарантирует, что количество столбцов, отображаемых в окне памяти, соответствует ширине окна памяти.</p>
<p>(Только меню) При закреплении или откреплении окно переходит в закрепленное состояние или выходит из него.</p>
<p>(Только меню) Переместить в новую док-станцию ​​закрывает окно Память и открывает его в новой док-станции.</p>
<p>(Только меню) Установить в качестве цели вкладки для типа окна устанавливает выбранное окно памяти в качестве цели вкладки для других окон памяти. Все окна памяти, которые открываются после того, как одно из них выбрано в качестве цели вкладки, автоматически группируются с этим окном в коллекции с вкладками.</p>
<p>(Только меню) Всегда плавающее окно остается незакрепленным, даже если его перетаскивают в место закрепления.</p>
<p>(Только меню) Перемещение с фреймом заставляет окно перемещаться при перемещении фрейма WinDbg, даже если окно не закреплено. Дополнительные сведения о закрепленных окнах, окнах с вкладками и плавающих окнах см. в разделе Расположение окон.</p>
<p>(Только меню) «Свойства» открывает диалоговое окно «Параметры памяти», которое описано в следующем разделе этой темы.</p>
<p>(Только меню) Справка открывает этот раздел в документации по средствам отладки для Windows.</p>
<p>(Только меню). Закрыть закрывает это окно.</p>
<h3>Диалоговое окно «Параметры памяти»</h3>
<p>При выборе пункта

В режиме ядра в этом диалоговом окне в виде вкладок доступны шесть типов памяти: виртуальная память, физическая память, данные шины, управляющие данные, ввод-вывод (информация о порте ввода-вывода) и MSR (зависимый от модели регистр). Информация). Выберите вкладку, соответствующую информации, к которой вы хотите получить доступ.

В пользовательском режиме доступна только вкладка "Виртуальная память".

Каждая вкладка позволяет вам указать память, которую вы хотите отобразить:

На вкладке "Виртуальная память" в поле "Смещение" укажите адрес или смещение начала диапазона памяти, который вы хотите просмотреть.

На вкладке «Физическая память» в поле «Смещение» укажите физический адрес начала диапазона памяти, который вы хотите просмотреть. Окно Память может отображать только описанную кэшируемую физическую память. Если вы хотите отобразить физическую память с другими атрибутами, используйте команду d* (отображение памяти) или расширение !d\*.

На вкладке "Данные шины" в меню "Тип данных шины" укажите тип данных шины. Затем используйте поля Номер шины, Номер слота и Смещение, чтобы указать данные шины, которые вы хотите просмотреть.

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

На вкладке «Ввод-вывод» в меню «Тип интерфейса» укажите тип интерфейса ввода-вывода. Используйте поля Номер шины, Адресное пространство и Смещение, чтобы указать данные, которые вы хотите просмотреть.

На вкладке MSR в поле MSR укажите регистр конкретной модели, который вы хотите просмотреть.

Каждая вкладка также содержит меню формата отображения. Это меню имеет тот же эффект, что и меню «Формат отображения» в окне «Память».

Нажмите "ОК" в диалоговом окне "Параметры памяти", чтобы изменения вступили в силу.

Дополнительная информация

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

Языки низкого уровня, такие как C, имеют примитивы ручного управления памятью, такие как malloc() и free(). Напротив, JavaScript автоматически выделяет память при создании объектов и освобождает ее, когда они больше не используются (сборка мусора). Этот автоматический подход может стать источником путаницы: у разработчиков может сложиться ложное впечатление, что им не нужно беспокоиться об управлении памятью.

Жизненный цикл памяти

Независимо от языка программирования жизненный цикл памяти практически всегда одинаков:

  1. Выделите необходимое количество памяти
  2. Использовать выделенную память (чтение, запись)
  3. Освобождать выделенную память, когда она больше не нужна

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

Распределение в JavaScript

Инициализация значения

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

Распределение через вызовы функций

Вызовы некоторых функций приводят к выделению объекта.

Некоторые методы выделяют новые значения или объекты:

Использование значений

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

Отпустить, когда память больше не нужна

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

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

Некоторые языки высокого уровня, такие как JavaScript, используют форму автоматического управления памятью, известную как сборка мусора (GC). Цель сборщика мусора — следить за выделением памяти и определять, когда блок выделенной памяти больше не нужен, и освобождать его. Этот автоматический процесс является приблизительным, поскольку общая проблема определения того, нужна ли еще конкретная часть памяти, неразрешима.

Сборка мусора

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

Ссылки

Основной концепцией, на которую опираются алгоритмы сборки мусора, является концепция ссылки. В контексте управления памятью говорят, что объект ссылается на другой объект, если первый имеет доступ ко второму (неявно или явно). Например, объект JavaScript имеет ссылку на свой прототип (неявная ссылка) и на значения своих свойств (явная ссылка).

В этом контексте понятие "объект" расширено до чего-то более широкого, чем обычные объекты JavaScript, и также включает области действия функции (или глобальную лексическую область действия).

Сборка мусора с подсчетом ссылок

Это самый наивный алгоритм сборки мусора. Этот алгоритм сводит проблему с определения того, нужен ли еще объект, к определению того, есть ли у объекта какие-либо другие объекты, ссылающиеся на него. Объект считается "мусорным" или коллекционным, если на него не ссылаются никакие ссылки.

Пример

Ограничение: циклические ссылки

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

Пример из жизни

Известно, что в Internet Explorer 6 и 7 есть сборщики мусора с подсчетом ссылок для объектов DOM. Циклы — распространенная ошибка, которая может привести к утечке памяти:

В приведенном выше примере элемент DOM "myDivElement" имеет циклическую ссылку на себя в свойстве "circularReference". Если свойство не удалено явным образом или не обнулено, сборщик мусора с подсчетом ссылок всегда будет иметь хотя бы одну неповрежденную ссылку и сохранит элемент DOM в памяти, даже если он был удален из дерева DOM. Если элемент DOM содержит большой объем данных (как показано в приведенном выше примере со свойством «lotsOfData»), память, потребляемая этими данными, никогда не будет освобождена, что может привести к проблемам, связанным с памятью, например к замедлению работы браузера.< /p>

Алгоритм маркировки и очистки

Этот алгоритм сводит определение "объект больше не нужен" к "объект недоступен".

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

Этот алгоритм является улучшением по сравнению с предыдущим, поскольку объект, имеющий нулевые ссылки, фактически недоступен. Обратное неверно, как мы видели с циклическими ссылками.

По состоянию на 2012 год все современные браузеры поставляются со сборщиком мусора с пометкой и очисткой. Все улучшения, сделанные в области сборки мусора JavaScript (генерационная/инкрементальная/параллельная/параллельная сборка мусора) за последние несколько лет, являются улучшениями реализации этого алгоритма, но не улучшениями самого алгоритма сборки мусора или его сокращением определения того, когда "объект больше не нужен".

Циклы больше не проблема

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

Ограничение: освобождение памяти вручную

Бывают случаи, когда было бы удобно вручную решить, когда и какая память освобождается. Чтобы освободить память объекта, его нужно явно сделать недоступным.

По состоянию на 2019 год невозможно явно или программно запустить сборку мусора в JavaScript.

Node.js

Node.js предлагает дополнительные параметры и инструменты для настройки и устранения проблем с памятью, которые могут быть недоступны для JavaScript, выполняемого в среде браузера.

Флаги двигателя V8

Максимальный объем доступной памяти кучи можно увеличить с помощью флага:

Мы также можем открыть сборщик мусора для отладки проблем с памятью с помощью флага и отладчика Chrome:

Есть два способа анализа использования памяти в вашем приложении в Unity:

  • Memory Profiler Окно, помогающее оптимизировать игру. Он показывает, сколько времени вы тратите на различные области вашей игры. Например, он может сообщать о проценте времени, затраченном на рендеринг, анимацию или игровую логику. Подробнее
    См. в модуле "Глоссарий". Этот встроенный модуль профилировщика предоставляет базовую информацию о том, где ваше приложение использует память.
  • Пакет Memory Profiler: это отдельный пакет, который вы можете добавить в свой проект. Он добавляет в редактор Unity дополнительное окно Memory Profiler, которое затем можно использовать для более подробного анализа использования памяти в приложении. Вы можете сохранять и сравнивать моментальные снимки, чтобы упростить поиск утечек памяти, или просматривать структуру памяти, чтобы находить проблемы с фрагментацией памяти.

На этой странице содержится информация о встроенном модуле Memory Profiler. Дополнительные сведения о пакете профилировщика памяти см. в документации по профилировщику памяти.

Модуль Memory Profiler визуализирует счетчики, представляющие общий объем выделенной памяти в вашем приложении. Вы можете использовать модуль памяти для просмотра такой информации, как количество загруженных объектов и общий объем памяти, который они занимают по категориям. Вы также можете увидеть количество выделений GC на кадр Profiler.

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

Категории диаграммы

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

< /th> < td style="text-align:left;">Количество материалов
Категория Описание
Всего выделено Общий объем памяти, использованной вашим приложением.< /td>
Память текстур Сколько памяти используется текстурами. Изображение при рендеринге GameObject, Sprite или элемента пользовательского интерфейса. Текстуры часто применяются к поверхности сетки, чтобы придать ей визуальную детализацию. Дополнительная информация
См. глоссарий в вашем приложении.
Mesh Основной графический примитив Unity. Меши составляют большую часть ваших 3D-миров. Unity поддерживает триангулированные или четырехугольные полигональные сетки. Поверхности Nurbs, Nurms, Subdiv должны быть преобразованы в полигоны. Дополнительная информация
См. в глоссарии. Память
Сколько памяти используется сетками в вашем приложении.
Количество материалов Ресурс, определяющий способ визуализации поверхности. Дополнительная информация
См. экземпляры глоссария в вашем приложении.
Количество объектов Количество экземпляров собственного объекта в вашем приложении.
Используемая память GC Объем памяти, используемый кучей GC.
GC, выделенный во фрейме< /td> Объем памяти, выделенный на кадр в куче GC.

Панель сведений о модуле

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

Простой вид

В простом представлении отображается обзор того, как Unity использует память в режиме реального времени для каждого кадра.

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

Простой вид модуля памяти

Представление модуля Simple Memory

Эта информация также доступна через API ProfilerRecorder и в редакторе модулей Profiler, поэтому вы можете добавить их в пользовательский модуль Profiler.

Доступность в проигрывателях

Вы можете использовать ProfilerRecorder API для доступа к счетчикам модуля Memory Profiler в Players. Следующий пример содержит простой сценарий, который собирает метрики «Общая зарезервированная память», «Зарезервированная память GC» и «Используемая системой память» и отображает их в виде GUI.TextArea. Информация о модуле Memory Profiler относится к категории ProfilerCategory.Memory Profiler. Идентифицирует данные рабочей нагрузки для подсистемы Unity (например, категории Rendering, Scripting и Animation). Unity применяет цветовое кодирование к категориям, чтобы визуально различать типы данных в окне Profiler.
См. в глоссарии.

На следующем снимке экрана показан результат добавления скрипта в Tanks! учебный проект.

Tanks! Учебное пособие с наложенной информацией о памяти

Танки! учебник с наложенной информацией о памяти

Эта информация доступна в Release Players, как и другие счетчики высокого уровня в таблице выше. Если вы хотите видеть выбранные счетчики памяти в пользовательском модуле в окне Profiler, используйте редактор модулей для настройки диаграммы.

Подробный просмотр

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

Подробный вид модуля памяти

Детальный вид модуля памяти

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

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

  • Другое: объекты, не являющиеся ни активами, ни игровыми объектами, ни компонентами. Сюда входит такая информация, как память, которую Unity использует для разных систем.
  • Не сохранено: объекты отмечены как "Не сохранять".
  • Встроенные ресурсы: ресурсы редактора Unity или ресурсы Unity по умолчанию, например шейдеры. Программа, работающая на графическом процессоре. Дополнительная информация
    См. глоссарий, который вы добавили в список всегда включенных шейдеров в настройках графики.
  • Ресурсы: ресурсы, на которые ссылается пользовательский или собственный код.
  • Память сцены: объекты и прикрепленные компоненты.

Когда вы нажимаете на игровой объект в списке ресурсов или памяти сцен, он выделяется в представлении проекта или сцены. Интерактивное представление мира, который вы создаете. Вы используете Scene View для выбора и размещения пейзажей, персонажей, камер, источников света и всех других типов игровых объектов. Подробнее
см. в глоссарии.

Примечание. В категории «Другое» память, указанная в разделе System.ExecutableAndDlls, является памятью только для чтения. Операционная система может отбрасывать эти страницы по мере необходимости, а затем повторно загружать их из файловой системы. Это снижает использование памяти и обычно не влияет напрямую на решение операционной системы закрыть ваше приложение, если оно использует слишком много памяти. Некоторые из этих страниц могут также использоваться совместно с другими приложениями, использующими те же платформы.

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

Джейсон Миллер

Бартек Новерски

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

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

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

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

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

Когда страница вызывает функцию window.open() для создания нового окна или вкладки браузера, возвращается объект Window, представляющий окно или вкладку. Даже после закрытия такого окна или его закрытия пользователем объект Window, возвращаемый функцией window.open(), все еще может использоваться для доступа к информации о нем. Это один из типов отсоединенных окон: поскольку код JavaScript потенциально может получить доступ к свойствам закрытого объекта Window, он должен храниться в памяти. Если окно содержит много объектов JavaScript или фреймов, эта память не может быть освобождена до тех пор, пока не исчезнут оставшиеся ссылки JavaScript на свойства окна. Использование Chrome DevTools для демонстрации возможности сохранения документа после закрытия окна.

Та же проблема может возникнуть при использовании элементов <iframe>. Элементы iframe ведут себя как вложенные окна, содержащие документы, а их свойство contentWindow обеспечивает доступ к содержащемуся объекту Window, подобно значению, возвращаемому функцией window.open(). Код JavaScript может сохранять ссылку на contentWindow или contentDocument iframe, даже если iframe удален из DOM или его URL-адрес изменился, что предотвращает сборку мусора документа, поскольку доступ к его свойствам по-прежнему возможен. Демонстрация того, как обработчик событий может сохранить документ iframe даже после перехода iframe на другой URL-адрес.

В случаях, когда ссылка на документ в окне или iframe сохраняется из JavaScript, этот документ будет храниться в памяти, даже если содержащее окно или iframe перейдет на новый URL-адрес. Это может быть особенно проблематично, когда JavaScript, содержащий эту ссылку, не обнаруживает, что окно/фрейм перешли к новому URL-адресу, поскольку он не знает, когда он становится последней ссылкой, сохраняющей документ в памяти.

При работе с окнами и окнами iframe в том же домене, что и основная страница, обычно прослушивание событий или доступ к свойствам происходит за пределами документа. Например, давайте вернемся к варианту примера средства просмотра презентаций из начала этого руководства. Средство просмотра открывает второе окно для отображения заметок докладчика. Окно заметок докладчика прослушивает события щелчка, чтобы перейти к следующему слайду. Если пользователь закрывает это окно заметок, JavaScript, работающий в исходном родительском окне, по-прежнему имеет полный доступ к документу заметок докладчика:

Представьте, что мы закрываем окно браузера, созданное функцией showNotes() выше. Нет обработчика событий, прослушивающего, чтобы обнаружить, что окно было закрыто, поэтому ничто не сообщает нашему коду, что он должен очистить любые ссылки на документ. Функция nextSlide() по-прежнему «живая», потому что она связана как обработчик кликов на нашей главной странице, а тот факт, что nextSlide содержит ссылку на notesWindow, означает, что на окно все еще ссылаются и его нельзя удалить сборщиком мусора. Иллюстрация того, как ссылки на окно предотвращают его сборку мусора после закрытия.

См. Решение: общаться через postMessage, чтобы узнать, как исправить эту конкретную утечку памяти.

Обработчики событий могут быть зарегистрированы в исходном документе iframe до того, как фрейм перейдет к своему предполагаемому URL-адресу, что приведет к случайным ссылкам на документ и сохранению iframe после очистки других ссылок.

Документ с большим объемом памяти, загруженный в окно или iframe, может случайно оставаться в памяти долгое время после перехода по новому URL-адресу. Это часто происходит из-за того, что родительская страница сохраняет ссылки на документ, чтобы можно было удалить прослушиватель.

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

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

Снимок экрана моментального снимка кучи в Chrome DevTools, показывающих ссылки, которые сохраняют большой объект». ширина=

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

Чтобы записать снимок кучи, перейдите на вкладку "Память" в Chrome DevTools и выберите "Снимок кучи" в списке доступных типов профилирования. После завершения записи в представлении «Сводка» отображаются текущие объекты в памяти, сгруппированные по конструктору. Демонстрация создания моментального снимка кучи в Chrome DevTools.

Попробуйте

Откройте это пошаговое руководство в новом окне.

Анализ дампов кучи может быть непростой задачей, а найти правильную информацию при отладке может быть довольно сложно.Чтобы помочь в этом, инженеры Chromium yossik@ и peledni@ разработали автономный инструмент очистки кучи, который может помочь выделить определенный узел, например отдельное окно. Запуск Heap Cleaner для трассировки удаляет другую ненужную информацию из графа хранения, что делает трассировку более чистой и более удобной для чтения.

Скриншот раздела пользовательский интерфейс Chrome DevTools». ширина=

Снимки кучи обеспечивают высокий уровень детализации и отлично подходят для определения мест утечек, но создание снимка кучи выполняется вручную. Еще один способ проверить наличие утечек памяти — получить текущий размер кучи JavaScript из API-интерфейса performance.memory: проверка используемого размера кучи JS в DevTools при создании, закрытии и отсутствии ссылки на всплывающее окно.

API performance.memory предоставляет информацию только о размере кучи JavaScript, что означает, что он не включает память, используемую документом и ресурсами всплывающего окна. Чтобы получить полную картину, нам нужно использовать новый API performance.measureUserAgentSpecificMemory(), который в настоящее время тестируется в Chrome.

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

Неустановленные ссылки, решения для мониторинга и удаления, а также решения WeakRef основаны на этом примере.

В следующем примере для открытия и закрытия всплывающего окна используются две кнопки. Чтобы кнопка «Закрыть всплывающее окно» работала, ссылка на открытое всплывающее окно хранится в переменной:

На первый взгляд кажется, что приведенный выше код позволяет избежать распространенных ошибок: не сохраняются ссылки на документ всплывающего окна и не регистрируются обработчики событий во всплывающем окне. Однако после нажатия кнопки «Открыть всплывающее окно» переменная всплывающего окна теперь ссылается на открытое окно, и эта переменная доступна из области действия обработчика нажатия кнопки «Закрыть всплывающее окно». Если всплывающее окно не переназначено или не удален обработчик кликов, заключенная в этом обработчике ссылка на всплывающее окно означает, что оно не может быть удалено сборщиком мусора.

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

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

Это помогает, но раскрывает еще одну проблему, характерную для окон, созданных с помощью open() : что, если пользователь закроет окно вместо того, чтобы нажать нашу пользовательскую кнопку закрытия? Более того, что, если пользователь начнет просматривать другие веб-сайты в открытом нами окне? Хотя изначально казалось достаточным сбросить ссылку на всплывающее окно при нажатии кнопки закрытия, по-прежнему существует утечка памяти, когда пользователи не используют эту конкретную кнопку для закрытия окна. Для решения этой проблемы необходимо обнаружить эти случаи, чтобы сбросить устаревшие ссылки, когда они возникают.

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

Внимание

Есть еще одно событие, называемое разгрузкой, которое похоже на скрытие страницы, но считается опасным, и его следует по возможности избегать. Дополнительные сведения см. в разделе Устаревшие API жизненного цикла, которых следует избегать: событие выгрузки.

Событие pagehide можно использовать для обнаружения закрытых окон и перехода от текущего документа. Однако есть одно важное предостережение: все вновь созданные окна и фреймы содержат пустой документ, а затем асинхронно переходят к заданному URL-адресу, если он предоставлен. В результате начальное событие скрытия страницы запускается вскоре после создания окна или фрейма, непосредственно перед загрузкой целевого документа. Поскольку наш эталонный код очистки должен запускаться, когда целевой документ выгружается, нам нужно игнорировать это первое событие скрытия страницы. Для этого существует ряд методов, самый простой из которых — игнорировать события скрытия страницы, исходящие из URL-адреса исходного документа about:blank. Вот как это будет выглядеть в нашем примере с всплывающим окном:

Важно отметить, что этот метод работает только для окон и фреймов, которые имеют то же эффективное происхождение, что и родительская страница, на которой выполняется наш код. При загрузке контента из другого источника оба местоположения.host и событие pagehide недоступны по соображениям безопасности. Хотя обычно лучше избегать ссылок на другие источники, в редких случаях, когда это требуется, можно отслеживать свойства window.closed или frame.isConnected. Когда эти свойства изменяются, указывая на закрытое окно или удаленный iframe, рекомендуется удалить любые ссылки на него.

WeakRef – это новая функция языка JavaScript, доступная в Firefox для настольных ПК, начиная с версии 79, и в браузерах на основе Chromium, начиная с версии 84. Поскольку она еще не получила широкой поддержки, это решение лучше подходит для отслеживания и устранения проблем, а не исправление их для производства.

JavaScript недавно получил поддержку нового способа ссылки на объекты, который позволяет выполнять сборку мусора, который называется WeakRef . WeakRef, созданный для объекта, не является прямой ссылкой, а представляет собой отдельный объект, предоставляющий специальный метод .deref(), возвращающий ссылку на объект, если он не был удален сборщиком мусора. С помощью WeakRef можно получить доступ к текущему значению окна или документа, но при этом разрешить его сборку мусора. Вместо того, чтобы сохранять ссылку на окно, которое должно быть удалено вручную в ответ на такие события, как pagehide или свойства, такие как window.closed , доступ к окну получается по мере необходимости. Когда окно закрывается, оно может быть удалено сборщиком мусора, в результате чего метод .deref() начинает возвращать значение undefined .

Одна интересная деталь, которую следует учитывать при использовании WeakRef для доступа к окнам или документам, заключается в том, что ссылка обычно остается доступной в течение короткого периода времени после закрытия окна или удаления iframe. Это связано с тем, что WeakRef продолжает возвращать значение до тех пор, пока связанный с ним объект не будет удален сборщиком мусора, что происходит асинхронно в JavaScript и обычно во время простоя. К счастью, при проверке отдельных окон на панели памяти Chrome DevTools создание моментального снимка кучи фактически запускает сборку мусора и удаляет окно со слабой ссылкой. Также можно проверить, что объект, на который ссылается WeakRef, был удален из JavaScript, либо обнаружив, что deref() возвращает значение undefined, либо используя новый API FinalizationRegistry:

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

Доступен более целостный альтернативный подход, позволяющий избежать устаревших ссылок между окнами и документами: установить разделение, ограничив взаимодействие между документами функцией postMessage() . Возвращаясь к нашему исходному примеру заметок докладчика, такие функции, как nextSlide(), обновляли окно заметок напрямую, ссылаясь на него и манипулируя его содержимым. Вместо этого основная страница могла бы передавать необходимую информацию в окно заметок асинхронно и косвенно через postMessage() .

Хотя это по-прежнему требует, чтобы окна ссылались друг на друга, ни одно из них не сохраняет ссылку на текущий документ из другого окна. Подход с передачей сообщений также поощряет проекты, в которых ссылки на окна хранятся в одном месте, а это означает, что только одна ссылка должна быть отменена, когда окна закрыты или уходят. В приведенном выше примере только функция showNotes() сохраняет ссылку на окно заметок и использует событие pagehide для очистки ссылки.

В тех случаях, когда открывается всплывающее окно, с которым вашей странице не нужно взаимодействовать или управлять, вы можете вообще не получать ссылку на окно. Это особенно полезно при создании окон или фреймов, которые будут загружать контент с другого сайта. В этих случаях window.open() принимает параметр "noopener", который работает так же, как атрибут rel="noopener" для ссылок HTML:

Опция "noopener" приводит к тому, что window.open() возвращает значение null , что делает невозможным случайное сохранение ссылки на всплывающее окно. Это также предотвращает получение всплывающим окном ссылки на его родительское окно, поскольку свойство window.opener будет иметь значение null .

Мы надеемся, что некоторые рекомендации в этой статье помогут найти и устранить утечку памяти. Если у вас есть другой метод отладки отдельных окон или эта статья помогла выявить утечки в вашем приложении, я был бы рад узнать об этом! Вы можете найти меня в Твиттере @_developit.

Читайте также: