Вкладка сайта тормозит со временем: как подтвердить утечку памяти

Пошагово проверяем рост памяти во вкладке: строим воспроизводимый сценарий, сравниваем Heap snapshot и находим удерживающие ссылки.

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

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

Сначала исключите разовую нагрузку

Рост потребления памяти сам по себе ещё не доказывает утечку. Страница может один раз загрузить шрифты, изображения, данные справочника или код нового раздела, а браузер — отложить сборку мусора.

Проверьте четыре признака вместе:

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

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

Подготовьте воспроизводимый сценарий

Ниже — порядок для Chrome и других браузеров на Chromium. В других браузерах названия инструментов могут отличаться, но принцип сравнения остаётся тем же.

  1. Откройте страницу в чистом профиле без лишних расширений. Используйте одну версию браузера, один тестовый аккаунт и одинаковый набор данных.
  2. Запишите короткую последовательность действий. Например: открыть карточку товара, показать галерею, закрыть её и вернуться к списку.
  3. Один раз выполните сценарий для прогрева. Так начальная загрузка ленивых модулей меньше искажает сравнение.
  4. Откройте DevTools, вкладку Memory, запустите сборку мусора и сделайте первый Heap snapshot.
  5. Повторите сценарий одинаковое число раз, снова запустите сборку мусора и снимите второй snapshot. Затем проведите ещё одну такую же серию и создайте третий снимок.

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

Как читать сравнение Heap snapshot

Сравнивайте второй снимок с первым, а третий — со вторым. Ищите не самый большой объект вообще, а типы объектов, количество и удерживаемый объём которых растут после каждой одинаковой серии.

Проверьте удерживающие ссылки

Поле Retainers показывает цепочку ссылок, из-за которой объект остаётся доступным для JavaScript. Идите по цепочке к владельцу: глобальному кэшу, массиву состояния, замыканию, подписке или обработчику события.

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

Ищите detached DOM

Detached-элемент уже удалён из видимого документа, но на него всё ещё ссылается JavaScript. Частые источники — обработчик события, закрытое модальное окно, ссылка из кэша или компонент, который не выполнил очистку при уничтожении.

Один detached-узел не равен дефекту. Важна повторяемость: после каждого открытия и закрытия интерфейса число одинаковых деревьев увеличивается, а цепочка Retainers ведёт к одному владельцу.

Используйте запись выделений точечно

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

Что чаще всего остаётся в памяти

Проверяйте код рядом с действием, после которого начинается накопление:

  • обработчик добавляется при каждом открытии компонента, но не снимается при закрытии;
  • таймер или requestAnimationFrame продолжает работать после ухода со страницы;
  • подписка на WebSocket, SSE или внутреннюю шину событий не закрывается;
  • замыкание удерживает DOM-узел или большой ответ API;
  • глобальный кэш растёт без лимита и стратегии удаления;
  • созданный через URL.createObjectURL() адрес файла не освобождается;
  • сторонний виджет повторно инициализируется при навигации внутри SPA.

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

Как не перепутать утечку с другой проблемой

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

Повторите сценарий в чистом профиле. Если проблема исчезла, включайте расширения по одному. Если тормозит только один набор данных, проверьте размер ответа и число элементов в DOM: странице может быть тяжело обрабатывать допустимый, но слишком большой объём.

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

Что передать разработчику

Короткий отчёт ускоряет проверку:

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

Heap snapshot может содержать строки страницы и данные текущей сессии. Не публикуйте его в открытом доступе; передавайте только по согласованному защищённому каналу и не используйте реальные персональные данные в тесте.

Как проверить исправление

Запустите тот же сценарий в тех же условиях. После прогрева и сборки мусора базовая линия должна перестать расти от серии к серии, а проблемные detached-деревья или подписки — перестать накапливаться.

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

Что контролировать после исправления

Браузерный сценарий и внешний мониторинг отвечают на разные вопросы. Web-Puls может регулярно проверять доступность публичного URL и время ответа, но обычная HTTP-проверка не воспроизводит долгую работу пользователя во вкладке и не доказывает отсутствие утечки памяти.

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

Мониторинг сайтов

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

Проверьте свой сайт прямо сейчас

Введите адрес сайта: Web-Puls покажет HTTP-код, время ответа и базовую диагностику. Для постоянного контроля можно подключить мониторинг.

Хотите видеть такую историю по своему сайту?

Добавьте сайт в Web-Puls: мы будем проверять доступность, SSL и содержимое страницы.