Управление памятью

Управление памятью в Google Maps JavaScript API определяется тем, как создаются, хранятся и уничтожаются объекты карты, слои, маркеры и обработчики событий. В средах одностраничных приложений и динамических интерфейсов ошибки в освобождении ресурсов приводят к постепенному росту потребления памяти, утечкам и деградации производительности, особенно при частой инициализации карты или смене представлений.

Экземпляр карты создаётся через конструктор google.maps.Map и привязывается к DOM-элементу. При этом внутри создаётся множество объектов: тайловые слои, обработчики событий, проекции, контролы интерфейса.

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

let map = new google.maps.Map(document.getElementById("map"), {
  center: { lat: 50.45, lng: 30.52 },
  zoom: 10
});

// корректное освобождение
map = null;

Однако простое обнуление переменной недостаточно, если остаются активные слушатели событий или внешние ссылки.

Обработчики событий и утечки памяти

Система событий google.maps.event создаёт сильные ссылки на обработчики. Если слушатели не удаляются явно, они продолжают удерживать объекты в памяти даже после удаления карты.

Типичный источник утечек — привязка событий к маркерам и карте без последующей очистки.

const listener = google.maps.event.addListener(map, "click", (e) => {
  console.log(e.latLng);
});

// удаление слушателя
google.maps.event.removeListener(listener);

При большом количестве динамически создаваемых объектов важно централизованно хранить ссылки на все подписки.

const listeners = [];

listeners.push(
  google.maps.event.addListener(map, "zoom_changed", handler)
);

// очистка всех событий
listeners.forEach(l => google.maps.event.removeListener(l));
listeners.length = 0;

Маркеры и освобождение ресурсов

Маркеры являются одними из наиболее ресурсоёмких объектов. Каждый маркер содержит DOM-слой, иконки, обработчики и внутренние состояния.

Удаление маркера из карты не освобождает его память полностью, если остаются ссылки.

marker.setMap(null);
marker = null;

При работе с массивами маркеров требуется явная очистка:

markers.forEach(m => m.setMap(null));
markers.length = 0;

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

InfoWindow и привязанные объекты

InfoWindow может удерживать ссылки на DOM-узлы и данные, переданные через content. При частом создании всплывающих окон без переиспользования возникает накопление неосвобождённых объектов.

if (infoWindow) {
  infoWindow.close();
  infoWindow = null;
}

При динамическом создании рекомендуется использовать один экземпляр InfoWindow на карту, переиспользуя его вместо создания новых объектов.

Работа с оверлеями

Оверлеи (OverlayView) требуют ручного управления жизненным циклом. Метод setMap(null) отключает отображение, но не всегда гарантирует немедленное освобождение памяти.

overlay.setMap(null);
overlay = null;

Если оверлей создаёт DOM-структуры через onAdd, необходимо дополнительно удалять эти элементы из DOM в onRemove, иначе ссылки останутся в памяти.

Overlay.prototype.onRem ove = function () {
  this.div.parentNode.removeChild(this.div);
  this.div = null;
};

Утечки через замыкания

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

function createHandler(map, largeDataset) {
  return function () {
    console.log(largeDataset.length);
  };
}

Даже после удаления карты largeDataset остаётся в памяти, если слушатель не удалён.

Решение заключается в минимизации захвата контекста и явном освобождении ссылок.

Управление слоями данных

Data Layer хранит геометрические объекты и их свойства. При частой перезагрузке данных без очистки слоя происходит накопление объектов.

map.data.forEach(feature => {
  map.data.remove(feature);
});

Полная очистка слоя перед загрузкой новых данных предотвращает рост памяти.

DirectionsService и PlacesService

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

Особенно критично хранение больших массивов place results без ограничения размера кэша.

Кэширование и контроль объёма данных

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

const cache = new Map();

function setCache(key, value) {
  if (cache.size > 100) {
    const firstKey = cache.keys().next().value;
    cache.delete(firstKey);
  }
  cache.set(key, value);
}

Работа в SPA и повторная инициализация карты

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

Последовательность освобождения:

  • удаление маркеров
  • удаление слушателей
  • очистка оверлеев
  • сброс слоёв данных
  • обнуление ссылки на карту
function destroyMap(map, markers, listeners) {
  markers.forEach(m => m.setMap(null));
  listeners.forEach(l => google.maps.event.removeListener(l));

  map.data.forEach(f => map.data.remove(f));

  map = null;
  markers.length = 0;
  listeners.length = 0;
}

Влияние рендеринга тайлов

Тайловая система карты использует кэширование изображений и DOM-слоёв. При частом создании и уничтожении карт браузер может не сразу освобождать текстуры и изображения.

Особенно заметно это при переключении между большим количеством карт с разными параметрами.

Минимизация количества одновременных экземпляров карты снижает нагрузку на GPU и память.

Использование WeakMap для ассоциаций

Для хранения дополнительных данных, связанных с объектами карты, целесообразно использовать WeakMap, чтобы избежать удержания объектов после их удаления.

const metadata = new WeakMap();

metadata.set(marker, { id: 1, type: "shop" });

При удалении маркера запись автоматически становится доступной для сборщика мусора.

Оптимизация большого количества маркеров

При отображении тысяч объектов создание отдельных маркеров приводит к резкому росту памяти. Альтернативой является кластеризация и виртуализация.

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

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

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

При переключении тем, слоёв или режимов отображения важно не только скрывать элементы, но и удалять их из памяти. Простое скрытие через setVisible(false) не освобождает ресурсы.

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

Контроль утечек через профилирование

Проблемы памяти в картографических приложениях выявляются через профилировщики памяти браузера. Типичные признаки утечек:

  • рост количества DOM-узлов при повторных инициализациях
  • увеличение количества слушателей событий
  • сохранение ссылок на удалённые маркеры
  • увеличение heap size после каждого открытия карты

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