Ответ
Эвакуация в контексте map
в Go — это процесс постепенного перемещения (миграции) пар ключ-значение из старой внутренней хеш-таблицы в новую, увеличенную в размере. Этот механизм является частью реализации map
и происходит автоматически, когда map
становится слишком заполненной.
Когда это происходит?
Эвакуация запускается не при простом переполнении, а когда коэффициент загрузки (load factor) превышает определенный порог. Коэффициент загрузки — это среднее количество элементов на одну "корзину" (bucket) в хеш-таблице. В Go этот порог равен 6.5.
Когда len(m) / len(buckets)
> 6.5, Go решает, что map
нужно расширить.
Как происходит процесс?
Процесс расширения и эвакуации спроектирован так, чтобы минимизировать задержки (паузы) в работе программы:
- Создание новой таблицы: Go выделяет память под новую хеш-таблицу, обычно в два раза больше старой.
- Инкрементальная эвакуация: Вместо того чтобы копировать все данные сразу (что вызвало бы заметную паузу), Go перемещает их постепенно. Эвакуация одной или двух старых корзин происходит при каждой операции записи (
map[key] = value
) или удаления (delete(m, key)
) вmap
. - Работа во время эвакуации:
- Запись: Новые элементы всегда добавляются в новую, увеличенную таблицу.
- Чтение и удаление: Сначала поиск происходит в новой таблице. Если ключ там не найден, поиск продолжается в старой таблице.
- Завершение: Когда все данные из старой таблицы перемещены в новую, указатель на старую таблицу обнуляется, и сборщик мусора (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 эффективно расти без значительных пауз, что критически важно для высокопроизводительных приложений.