Что такое процесс эвакуации в `map` в Go и когда он происходит?

Ответ

Эвакуация в map в Go — это механизм постепенного (инкрементального) перераспределения данных из старых бакетов (buckets) в новые, который запускается при росте хэш-таблицы. Это позволяет избежать длительных пауз ("stop-the-world") при изменении размера map.

Когда происходит эвакуация?

Процесс начинается, когда среднее количество элементов на один бакет (load factor) превышает 6.5.

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

  1. Аллокация новой таблицы: Создается новая, обычно в два раза большая, хэш-таблица.
  2. Постепенное копирование: Вместо того чтобы сразу копировать все данные, Go делает это постепенно. При каждой операции записи (а иногда и чтения) в map, один или два старых бакета "эвакуируются" — их содержимое переносится в новые бакеты.
  3. Сосуществование: На время эвакуации старая и новая таблицы существуют одновременно. Операции чтения могут проверять оба набора бакетов.
  4. Завершение: Когда все данные из старых бакетов перенесены, старая таблица освобождается.

Пример, который может инициировать эвакуацию:

// Создаем map с начальной емкостью 8 бакетов
m := make(map[int]string)

// При добавлении элементов load factor будет расти.
// После добавления ~52 элементов (8 * 6.5), начнется эвакуация.
for i := 0; i < 100; i++ {
    m[i] = fmt.Sprintf("value%d", i)
}

Ключевое преимущество: Эвакуация обеспечивает предсказуемую производительность, распределяя нагрузку по изменению размера map во времени, что критически важно для систем с низкими задержками (low-latency).