Что такое эвакуация (evacuation) данных в `map` в Go и как она происходит?

Ответ

Эвакуация в контексте map в Go — это процесс постепенного перемещения (миграции) пар ключ-значение из старой внутренней хеш-таблицы в новую, увеличенную в размере. Этот механизм является частью реализации map и происходит автоматически, когда map становится слишком заполненной.

Когда это происходит?

Эвакуация запускается не при простом переполнении, а когда коэффициент загрузки (load factor) превышает определенный порог. Коэффициент загрузки — это среднее количество элементов на одну "корзину" (bucket) в хеш-таблице. В Go этот порог равен 6.5.

Когда len(m) / len(buckets) > 6.5, Go решает, что map нужно расширить.

Как происходит процесс?

Процесс расширения и эвакуации спроектирован так, чтобы минимизировать задержки (паузы) в работе программы:

  1. Создание новой таблицы: Go выделяет память под новую хеш-таблицу, обычно в два раза больше старой.
  2. Инкрементальная эвакуация: Вместо того чтобы копировать все данные сразу (что вызвало бы заметную паузу), Go перемещает их постепенно. Эвакуация одной или двух старых корзин происходит при каждой операции записи (map[key] = value) или удаления (delete(m, key)) в map.
  3. Работа во время эвакуации:
    • Запись: Новые элементы всегда добавляются в новую, увеличенную таблицу.
    • Чтение и удаление: Сначала поиск происходит в новой таблице. Если ключ там не найден, поиск продолжается в старой таблице.
  4. Завершение: Когда все данные из старой таблицы перемещены в новую, указатель на старую таблицу обнуляется, и сборщик мусора (GC) освобождает занимаемую ею память.
// Создаем map с начальной емкостью 8 корзин (buckets)
m := make(map[int]string)

// Начинаем заполнять map
for i := 0; i < 100; i++ {
    m[i] = fmt.Sprintf("value-%d", i)
    // При некоторых из этих вставок будет срабатывать
    // триггер расширения и инкрементальная эвакуация.
    // Программа при этом не останавливается.
}

Этот "ленивый" подход к миграции данных позволяет map в Go эффективно расти без значительных пауз, что критически важно для высокопроизводительных приложений.