Отладка утечек памяти в веб-приложениях - сложная задача. Инструменты существуют, но они сложны, громоздки и часто не дают ответа на простой вопрос: почему у моего приложения происходит утечка памяти?

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

Когда я впервые начал изучать утечки памяти, я подумал, что это редкость. Как может JavaScript - язык с автоматическим сборщиком мусора - стать большим источником утечек памяти? Но чем больше я узнавал, тем больше подозревал, что утечки памяти на самом деле довольно часты в одностраничных приложениях (SPA) - просто никто не проверяет это!

Поскольку большинство веб-разработчиков не возятся с инструментами памяти Chrome для удовольствия, они, вероятно, не заметят утечки, пока вкладка браузера не выйдет из строя с ошибкой Out Of Memory, или страница не замедлится. Откройте диспетчер задач и обратите внимание, что веб-сайт использует много мегабайт (или даже гигабайт!) памяти.

Я уже писал об утечках памяти в прошлом, но мой совет в основном сводится к следующему: «Используйте Chrome DevTools, выполните это большое количество утомительных шагов, и тогда, возможно, вы сможете выяснить, почему на вашей странице происходит утечка памяти». Было бы намного лучше, если бы инструмент мог автоматически обнаруживать утечки памяти.

Вот почему я написал fuite (по-французски «утечка»). fuite- это инструмент командной строки, которому можно указать на любой URL-адрес, и он будет анализировать страницу на предмет утечек памяти:

npx fuite <https://example.com>

Вот и все! По умолчанию он предполагает, что сайт является клиентским SPA, и будет сканировать страницу на предмет внутренних ссылок (таких как /aboutили /contact). Затем для каждой ссылки выполняются следующие шаги:

  1. Нажатие на ссылку

  2. Нажатие кнопки "назад" в браузере

  3. Повтор, чтобы проанализировать есть ли утечка

Fuiteпри обнаружении утечек, покажет какие объекты предположительно вызывают утечку:

Для этого fuiteиспользует следующую стратегию:

  • запуск Chrome

  • запуск некоторого сценарий п число раз (7 по умолчанию) и анализ есть ли кратное увеличение у разных объектов

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

Тестирование реальных веб-сайтов

Несколько удивительно, что “базового” сценария перехода по внутренним ссылкам и нажатия кнопки "Назад" достаточно, чтобы обнаружить утечки памяти во многих SPA. Я протестировал fuite на сайтах 10 популярных интерфейсных фреймворков и обнаружил утечки во всех из них:

В этом случае “внутренние ссылки” относятся к количеству протестированных внутренних ссылок, “средний рост” относится к среднему росту памяти для каждой ссылки (т.е. щелчок по ней, а затем нажатие кнопки “Назад”), а "максимальный рост" относится к той внутренней ссылке, которая протекала больше всего. Обратите внимание, что эти цифры не включают единовременные затраты на настройку, так как fuite выполняет одну подготовительную итерацию перед обычными 7 итерациями.

Чтобы подтвердить эти результаты самостоятельно, вы можете использовать вкладку Chrome DevTools Memory . Вот heap snapshot худшего сайта из моего набора, где я нажимаю ссылку, нажимаю кнопку возврата, делаю heap snapshot и повторяю:

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

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

Предостережения

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

Некоторый рост памяти также может быть связан с внутренними изменениями браузера (такими как JITing), которые веб-страница не может контролировать. Таким образом, цифры роста памяти - несовершенная мера того, что вы можете получить от устранения утечек - вполне может быть, что прирост на несколько килобайт неизбежен. (Хотя fuiteпытается игнорировать внутренний рост браузера и будет говорить «утечки обнаружены» только в том случае, если есть действенный совет для веб-разработчика.)

В редких случаях некоторый рост памяти также может быть связан с явными ошибками браузера. Анализируя сайты выше, я действительно нашел один (Сайт №4), который, похоже, страдает от этой ошибки Chrome из-за того, что <img loading="lazy">не выгружается. К сожалению, fuiteобнаружить ошибки браузера будет сложно , поэтому, если вы озадачены утечкой, рекомендуется провести перекрестную проверку с другими браузерами!

Также обратите внимание, что утечка многостраничного приложения (MPA) практически невозможна, потому что браузер очищает память при каждой навигации по страницам. ( Конечно, при условии отсутствия ошибок в браузере .) Во время тестирования я обнаружил две среды внешнего интерфейса, домашние страницы которых были MPA, и, что неудивительно, fuiteне смог найти в них никаких утечек. Они были исключены из результатов выше.

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

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

Другие сценарии утечки памяти

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

  • Открыть модальное диалоговое окно, а затем закрыть его

  • Навести указатель мыши на элемент, чтобы отобразить всплывающую подсказку, затем навести курсор мыши, чтобы закрыть его

  • Прокрутить список с бесконечной загрузкой, затем перейти назад и назад

  • И Т. Д.

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

Для анализа утечек fuiteзахватывает файлы моментальных heap snapshots , которые вы можете загрузить в Chrome DevTools для проверки. В нем также есть --debugрежим, который можно использовать для более детального анализа: пошаговое выполнение теста во время его выполнения, отладка браузера в режиме реального времени, анализ утечек объектов и т. Д.

Под капотом fuiteэто довольно простой инструмент, и я не буду утверждать, что он может выполнять 100% работы по устранению утечек памяти. По-прежнему существует человеческий компонент - выяснить, почему ваши объекты были выделены и сохранены, а затем найти разумное решение. Но моя цель состоит в том, чтобы автоматизировать ~ 95% работы по поиску "протекающих" мест в приложении.

Выражаю огромную благодарность моей жене за помощь в подготовке перевода

Комментарии (1)


  1. Bromles
    23.12.2021 06:20

    А можно хотя бы текст вычитывать перед копипастой из гугл переводчика на Хабр?