08

Потребление памяти веб-приложением

Потребление памяти веб-приложением

Цель: изучить основные шаблоны, приводящие к утечкам памяти в веб-приложениях при работе с DOM-деревом и обработчиками событий.

Вам предстоит смоделировать типичные ошибки, научиться использовать вкладку Memory/Память в инструментах разработчика для их обнаружения и описать механизмы возникновения утечек.

Критерии оценивания

Оценка за работу зависит от количества проанализированных сценариев и глубины работы с инструментами профилирования.

  • 30 баллов выставляется за реализацию одного сценария утечки, снятие базового снимка памяти (Heap Snapshot) и демонстрацию роста потребления.
  • 50 баллов требует реализации всех предложенных сценариев, использования различных режимов профилирования (например, Allocation Timeline) и поиска конкретных “отсоединенных” (detached) узлов в памяти.
  • 60 баллов ставится за подробный отчет, в котором применена техника “трех снимков”, указаны точные причины удержания объектов в памяти сборщиком мусора для каждого сценария и предложены конкретные способы устранения утечек.

Действия в репозитории

Как и в прошлых работах, переключитесь на ветку main, cоздайте ветку lab8 и работайте на ней. Создайте документ html с H1 заголовком “ФИО - Лабораторная работа №8”. Выполните эксперименты и заполните отчет в md-файле.

Требования к экспериментам

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

Забытые обработчики событий

Реализуйте функционал добавления в DOM большого количества сложных элементов (например, карточек с текстом и изображениями). При создании каждой карточки добавляйте обработчик события (например, scroll или resize) не на саму карточку, а на глобальный объект window или document. Внутри обработчика ссылайтесь на DOM-узел созданной карточки. Затем реализуйте функцию очистки, которая удаляет карточки из DOM-дерева, но намеренно “забывает” снять слушатели событий с глобального объекта. При многократном добавлении и удалении карточек память должна непрерывно расти.

Сохранение DOM-узлов в глобальных структурах

Создайте глобальный словарь (объект или Map) или массив. При генерации новых элементов интерфейса сохраняйте ссылки на эти DOM-узлы в вашу глобальную структуру под видом “кэширования для быстрого доступа”. После того как элементы интерфейса удаляются со страницы с помощью методов очистки контейнера, не удаляйте ссылки на них из глобального массива/словаря.

Утечка через замыкания (Closures)

Создайте функцию, которая генерирует крупный DOM-элемент. Внутри этой функции объявите таймер через setInterval, который будет выполнять какую-либо фоновую задачу и использовать переменные или элементы, созданные во внешней функции. Удалите сгенерированный элемент со страницы, но не останавливайте таймер (clearInterval). Замыкание таймера будет вечно удерживать ссылку на удаленный DOM-узел, не давая сборщику мусора его очистить.

Требования к отчету

Результаты оформляются в виде Markdown-файла.

  • Для каждого сценария опишите алгоритм ваших действий в браузере (нажатие кнопок, снятие снимков).
  • Обязательно вставьте снимки экрана (скриншоты) из вкладки Memory инструментов разработчика (DevTools). На скриншотах должно быть четко видно увеличение размера кучи (Heap size) между этапами.
  • Продемонстрируйте на скриншотах элементы, которые были удалены со страницы, но всё ещё присутствуют в оперативной памяти браузера.
  • Сделайте выводы для каждого сценария: объясните словами, какая именно ссылка (кто на кого ссылается) мешает Garbage Collector (сборщику мусора) освободить память, и как правильно переписать логику, чтобы устранить утечку.

Советы

  • Прежде чем делать снимок кучи (Take snapshot), всегда нажимайте кнопку принудительного вызова сборщика мусора (обычно иконка мусорного бака во вкладке Memory). Это гарантирует, что вы видите реальную утечку, а не просто объекты, до которых сборщик еще не успел добраться.
  • Как и при замерах скорости, профилируйте память строго в режиме “Инкогнито” без расширений. Расширения внедряют свои скрипты и DOM-узлы на страницу, создавая огромный шум в снимках памяти.
  • Когда вы найдете в снимке “отсоединенный” (Detached) элемент, обратите внимание на нижнюю панель “Retainers”. Она показывает путь от глобального объекта (window) до вашего утекшего узла, что помогает быстро найти ту самую забытую переменную или обработчик события.

Метод трех снимков

Эффективный способ найти утечку.

  1. Сделайте первый снимок (базовое состояние).
  2. Выполните действие (добавьте элементы) и откатите его (удалите элементы). Нажмите кнопку сборки мусора.
  3. Сделайте второй снимок.
  4. Повторите шаг 2 и сделайте третий снимок.

Сравнив третий снимок со вторым (выбрав фильтр Objects allocated between Snapshot 2 and Snapshot 3), вы точно увидите объекты, которые “застряли” в памяти после завершения цикла работы интерфейса.

Результат работы

Код в репозитории для выполнения замеров, отчет в markdown со снимками экранов и выводами.