Memory profiling

Особенности работы памяти в WebGL-рендерере

Mapbox GL JS использует WebGL как основной слой отрисовки, что означает распределение памяти между JavaScript-heap и GPU-памятью. Эти две области нельзя анализировать одинаково: стандартные инструменты браузера видят только часть картины.

JavaScript-объекты (карта, источники, стили, события) живут в heap V8, тогда как геометрия тайлов, текстуры, спрайты и glyph-атласы размещаются в виде GPU-ресурсов. Это создаёт ключевую особенность: утечки могут происходить даже при «чистом» JavaScript-heap, если не освобождаются WebGL-ресурсы.

Mapbox GL JS дополнительно использует Web Workers для парсинга векторных тайлов и подготовки данных к рендерингу. Эти воркеры имеют собственные буферы и кэш, который не всегда очевиден при стандартном профилировании.


Основные источники потребления памяти

Векторные тайлы и кэширование

Каждый загруженный тайл декодируется и сохраняется в памяти для повторного использования. Кэш тайлов является одной из крупнейших статей потребления памяти.

Рост памяти происходит при:

  • увеличении количества источников (sources)
  • увеличении maxzoom и плотности данных
  • перемещении карты по большим областям без очистки кэша

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


Растровые источники и текстуры

Растровые слои создают текстуры в GPU. Каждое изображение тайла преобразуется в texture object. Утечки часто возникают при:

  • частой смене raster-source
  • динамическом добавлении слоёв без удаления
  • отсутствии корректного вызова map.remove()

Текстуры не всегда немедленно освобождаются сборщиком мусора JavaScript, так как управляются WebGL-контекстом.


Шрифты и glyph atlas

Mapbox GL JS использует подписные наборы шрифтов, которые загружаются в виде glyph atlas — специальных текстур, содержащих символы.

Проблемные сценарии:

  • использование большого числа языков одновременно
  • динамическая смена text-field с разными наборами символов
  • загрузка кастомных шрифтов без контроля кэширования

Glyph atlas может занимать значительный объём GPU памяти, особенно при наличии CJK-символов.


Спрайты и изображения

Sprite atlas — ещё один крупный источник памяти. Он содержит иконки и изображения стиля.

Ключевая проблема возникает при:

  • частом вызове addImage() без removeImage()
  • динамической генерации иконок
  • смене стиля без освобождения предыдущего

Каждое изображение упаковывается в текстуру, которая остаётся в GPU до уничтожения стиля или карты.


Утечки памяти в жизненном цикле карты

Создание множества экземпляров Map

Наиболее частая проблема — создание нового mapboxgl.Map без уничтожения предыдущего экземпляра.

Даже если DOM-узел удалён, WebGL-контекст может оставаться активным, если не вызвано:

map.remove();

Без этого вызова:

  • остаются Web Workers
  • продолжается хранение tile cache
  • WebGL context может не освобождаться

События и замыкания

Слушатели событий могут удерживать ссылки на карту и связанные объекты.

Особенно критично:

  • map.on('move', ...)
  • map.on('render', ...)
  • кастомные обработчики в React/Vue обёртках

Если обработчики создаются внутри функций без последующего off, возникает классическая утечка через closure.


Динамическое управление источниками

Добавление и удаление источников (addSource, removeSource) влияет на память несимметрично.

Типичная проблема:

  • источник удалён из стиля
  • но связанные тайлы остаются в кэше
  • GPU ресурсы не освобождены сразу

Особенно заметно при frequent updates GeoJSON sources.


Инструменты профилирования

Chrome DevTools Memory

Основной инструмент — вкладка Memory:

  • Heap Snapshot — анализ JavaScript объектов
  • Allocation instrumentation timeline — отслеживание роста памяти
  • Comparison snapshot — поиск утечек между состояниями

Важно учитывать, что WebGL ресурсы не всегда видны в heap snapshot.


Performance Panel

Позволяет анализировать:

  • частоту пересборки тайлов
  • нагрузку на main thread
  • работу Web Workers

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


WebGL debugging

Дополнительные инструменты браузера позволяют отслеживать:

  • количество активных текстур
  • состояние WebGL context
  • утечки GPU памяти

В некоторых случаях полезно использовать расширения для отслеживания WebGL calls.


Типовые сценарии утечек

Смена стилей

Частая операция:

map.setStyle('mapbox://styles/...')

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

  • изображения
  • источники
  • слои

При многократной смене стилей наблюдается рост GPU памяти.


React/Vue интеграции

При использовании Mapbox GL JS внутри компонентных систем типична ошибка:

  • карта создаётся в useEffect
  • но не уничтожается в cleanup-функции

Это приводит к накоплению WebGL контекстов при повторных рендерах компонентов.


Динамическая геометрия

GeoJSON источники, обновляемые каждую секунду (например, трекинг объектов), могут:

  • увеличивать память при отсутствии оптимизации
  • создавать новые объекты вместо обновления существующих

Особенно критично при использовании setData без контроля размера данных.


Стратегии контроля памяти

Явное уничтожение ресурсов

Базовая стратегия — строгое управление жизненным циклом:

  • map.remove() при уничтожении карты
  • удаление источников и слоёв перед сменой стиля
  • освобождение изображений через removeImage

Ограничение кэша тайлов

Mapbox GL JS позволяет контролировать кэширование через параметры источников и стратегии загрузки.

Снижение памяти достигается за счёт:

  • уменьшения maxzoom
  • ограничения области просмотра
  • оптимизации tile sources

Переиспользование данных

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

  • обновлять GeoJSON через setData
  • переиспользовать изображения
  • избегать повторного добавления слоёв

Контроль частоты обновлений

Высокочастотные обновления (например, анимации или live data) требуют:

  • throttling или debouncing
  • батчинга изменений
  • минимизации пересоздания источников

Косвенные признаки утечек

Поведение приложения часто даёт больше информации, чем инструменты:

  • постепенное падение FPS
  • рост времени загрузки тайлов
  • увеличение задержки реакции интерфейса
  • нестабильность WebGL контекста

Особенно характерен сценарий, при котором перезагрузка страницы временно «исправляет» проблему — это почти всегда утечка ресурсов.


Особенности WebGL-контекста

WebGL контекст имеет ограничение на число активных контекстов в браузере. При превышении лимита:

  • новые карты не создаются
  • появляется ошибка too many WebGL contexts
  • GPU память становится недоступной

Это делает корректное уничтожение карт критически важным элементом архитектуры приложений на Mapbox GL JS.