Отладка утечек памяти в веб-приложениях - сложная задача. Инструменты существуют, но они сложны, громоздки и часто не дают ответа на простой вопрос: почему у моего приложения происходит утечка памяти?
Из-за этого, я готов поспорить, что большинство веб-разработчиков не ведут активного мониторинга утечек памяти. И, конечно же, если вы что-то не тестируете, ошибки могут легко проскользнуть.
Когда я впервые начал изучать утечки памяти, я подумал, что это редкость. Как может JavaScript - язык с автоматическим сборщиком мусора - стать большим источником утечек памяти? Но чем больше я узнавал, тем больше подозревал, что утечки памяти на самом деле довольно часты в одностраничных приложениях (SPA) - просто никто не проверяет это!
Поскольку большинство веб-разработчиков не возятся с инструментами памяти Chrome для удовольствия, они, вероятно, не заметят утечки, пока вкладка браузера не выйдет из строя с ошибкой Out Of Memory, или страница не замедлится. Откройте диспетчер задач и обратите внимание, что веб-сайт использует много мегабайт (или даже гигабайт!) памяти.
Я уже писал об утечках памяти в прошлом, но мой совет в основном сводится к следующему: «Используйте Chrome DevTools, выполните это большое количество утомительных шагов, и тогда, возможно, вы сможете выяснить, почему на вашей странице происходит утечка памяти». Было бы намного лучше, если бы инструмент мог автоматически обнаруживать утечки памяти.
Вот почему я написал fuite
(по-французски «утечка»). fuite
- это инструмент командной строки, которому можно указать на любой URL-адрес, и он будет анализировать страницу на предмет утечек памяти:
npx fuite <https://example.com
>
Вот и все! По умолчанию он предполагает, что сайт является клиентским SPA, и будет сканировать страницу на предмет внутренних ссылок (таких как /about
или /contact
). Затем для каждой ссылки выполняются следующие шаги:
Нажатие на ссылку
Нажатие кнопки "назад" в браузере
Повтор, чтобы проанализировать есть ли утечка
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% работы по поиску "протекающих" мест в приложении.
Выражаю огромную благодарность моей жене за помощь в подготовке перевода
Bromles
А можно хотя бы текст вычитывать перед копипастой из гугл переводчика на Хабр?