Как Go-мапа (map) растет и когда это происходит? Опишите процесс rehashing.

Ответ

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

Триггеры для rehashing:

  1. Коэффициент загрузки (Load Factor): Среднее количество элементов на один бакет превышает 6.5.
  2. Переполнение: Слишком много бакетов используют свои "дополнительные" бакеты (overflow buckets).

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

  1. Создание нового хранилища: Go runtime выделяет новый массив бакетов, в два раза большего размера.
  2. Инкрементальное копирование: Перенос данных из старых бакетов в новые происходит не сразу, а постепенно (амортизированно), чтобы избежать длительных пауз в работе приложения.
    • При каждой операции записи или удаления из мапы, Go переносит данные из одного или двух старых бакетов в новые.
    • При чтении из мапы, если ключ находится в бакете, который еще не перенесен, поиск происходит и в старом, и в новом месте.
  3. Завершение: Когда все данные скопированы, старый массив бакетов освобождается сборщиком мусора.

Важная особенность:

  • Сама по себе мапа в Go не является потокобезопасной. Для конкурентного доступа к мапе из нескольких горутин необходимо использовать внешнюю синхронизацию, например, sync.RWMutex или использовать sync.Map.

Ответ 18+ 🔞

А, вот эта тема про рост мапы в Go! Ну, слушай, история интересная, как бабушкин сундук — с виду просто, а внутри пиздец какие механизмы.

Представь себе, мапа — это такой шкаф с ящиками. Сначала ящиков немного, ты туда вещи аккуратно складываешь. Но потом ты, сука, такой жадный, начинаешь пихать туда всё подряд: носки, утюги, кота, старые пивные крышки. В какой-то момент ящики не закрываются, всё вываливается. Вот это и есть триггер для rehashing'а, или, по-нашему, «эвакуации». Мапа говорит: «Ёпта, я больше не могу!»

Когда конкретно она взрывается?

  1. Коэффициент загрузки. Это когда в среднем на каждый ящик навалили больше 6.5 вещей. Представь, в каждый ящик по 7 пар носков — уже пиздец, дверца не закрывается.
  2. Переполнение допов. У каждого ящика есть ещё маленькие коробочки сверху (overflow buckets). И когда эти коробочки тоже все забиты под завязку — всё, приехали, пора расширяться.

А как этот цирк происходит?

  1. Новый шкаф. Go runtime, такой хитрожопый, не начинает всё перекладывать тут же. Он сначала тихонечко, как партизан, заказывает новый шкаф, в два раза больше. Старый пока на месте стоит.
  2. Инкрементальное копирование (или «не ебём мозг сразу»). Вот тут гениальность, блядь. Они не вываливают весь хлам на пол и не перебирают его за раз. Нет! Они делают это постепенно, под шумок.
    • Каждый раз, когда ты что-то записываешь или удаляешь из мапы, она тебе под носом, пока ты не видишь, перекладывает вещи из одного-двух старых ящиков в новые.
    • Когда ты читаешь, а ключ ещё в старом ящике, она тебя не посылает, а честно ищет и там, и в новом месте. Умная, сука, система!
  3. Финал. Когда весь хлам перетащен, старый шкаф выносят на помойку (сборщик мусора его прибирает). И ты живёшь уже с новым, просторным.

И главная, блядь, особенность, которую все забывают:

Сама по себе эта мапа — не потокобезопасная тётка. Если к ней со всех сторон полезет толпа горутин — одна будет писать, другая читать, третья удалять — получится пиздец, бардак и паника. Она не умеет с этим сама. Поэтому либо ты ставишь охранника — sync.RWMutex (чтоб не все сразу), либо идёшь в специальный магазин за готовым шкафом с замком — sync.Map. А иначе — сам дурак, чувак. Всё развалится, как карточный домик.