Потребление памяти веб-приложением
Цель: изучить основные шаблоны, приводящие к утечкам памяти в веб-приложениях при работе с 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) до вашего утекшего узла, что помогает быстро найти ту самую забытую переменную или обработчик события.
Метод трех снимков
Эффективный способ найти утечку.
- Сделайте первый снимок (базовое состояние).
- Выполните действие (добавьте элементы) и откатите его (удалите элементы). Нажмите кнопку сборки мусора.
- Сделайте второй снимок.
- Повторите шаг 2 и сделайте третий снимок.
Сравнив третий снимок со вторым (выбрав фильтр Objects allocated between Snapshot 2 and Snapshot 3), вы точно увидите объекты, которые “застряли” в памяти после завершения цикла работы интерфейса.
Результат работы
Код в репозитории для выполнения замеров, отчет в markdown со снимками экранов и выводами.