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

Ответ

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

Основной случай — рост map:

Эвакуация происходит, когда map нуждается в расширении. Это случается, если коэффициент загрузки (load factor) превышает определённое пороговое значение (в текущей реализации Go — 6.5).

Процесс выглядит так:

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

Такой постепенный подход позволяет избежать больших пауз (stop-the-world) при работе с очень большими map.

Пример, который инициирует рост:

// Создаем map с начальной ёмкостью для 1 элемента
m := make(map[int]string, 1)

// Добавляем элементы. В какой-то момент
// коэффициент загрузки превысит 6.5, и начнется процесс роста и эвакуации.
m[0] = "a"
m[1] = "b"
m[2] = "c"
// ... и так далее
m[7] = "h" // К этому моменту, скорее всего, уже произойдет расширение

Важное уточнение про GC:

Иногда эвакуацию ошибочно связывают с работой сборщика мусора (GC) на куче. Однако, в отличие от GC в некоторых других языках (например, Java), сборщик мусора в Go является неперемещающим (non-moving). Это значит, что он не перемещает объекты в куче для её уплотнения (компактификации). Поэтому термин "эвакуация" к работе GC в Go обычно не применяется.