Ответ
Рост мапы в Go происходит, когда она становится слишком заполненной. Этот процесс называется rehashing (или эвакуация) и заключается в увеличении количества бакетов (buckets) в два раза.
Триггеры для rehashing:
- Коэффициент загрузки (Load Factor): Среднее количество элементов на один бакет превышает
6.5. - Переполнение: Слишком много бакетов используют свои "дополнительные" бакеты (overflow buckets).
Как происходит процесс:
- Создание нового хранилища: Go runtime выделяет новый массив бакетов, в два раза большего размера.
- Инкрементальное копирование: Перенос данных из старых бакетов в новые происходит не сразу, а постепенно (амортизированно), чтобы избежать длительных пауз в работе приложения.
- При каждой операции записи или удаления из мапы, Go переносит данные из одного или двух старых бакетов в новые.
- При чтении из мапы, если ключ находится в бакете, который еще не перенесен, поиск происходит и в старом, и в новом месте.
- Завершение: Когда все данные скопированы, старый массив бакетов освобождается сборщиком мусора.
Важная особенность:
- Сама по себе мапа в Go не является потокобезопасной. Для конкурентного доступа к мапе из нескольких горутин необходимо использовать внешнюю синхронизацию, например,
sync.RWMutexили использоватьsync.Map.
Ответ 18+ 🔞
А, вот эта тема про рост мапы в Go! Ну, слушай, история интересная, как бабушкин сундук — с виду просто, а внутри пиздец какие механизмы.
Представь себе, мапа — это такой шкаф с ящиками. Сначала ящиков немного, ты туда вещи аккуратно складываешь. Но потом ты, сука, такой жадный, начинаешь пихать туда всё подряд: носки, утюги, кота, старые пивные крышки. В какой-то момент ящики не закрываются, всё вываливается. Вот это и есть триггер для rehashing'а, или, по-нашему, «эвакуации». Мапа говорит: «Ёпта, я больше не могу!»
Когда конкретно она взрывается?
- Коэффициент загрузки. Это когда в среднем на каждый ящик навалили больше 6.5 вещей. Представь, в каждый ящик по 7 пар носков — уже пиздец, дверца не закрывается.
- Переполнение допов. У каждого ящика есть ещё маленькие коробочки сверху (overflow buckets). И когда эти коробочки тоже все забиты под завязку — всё, приехали, пора расширяться.
А как этот цирк происходит?
- Новый шкаф. Go runtime, такой хитрожопый, не начинает всё перекладывать тут же. Он сначала тихонечко, как партизан, заказывает новый шкаф, в два раза больше. Старый пока на месте стоит.
- Инкрементальное копирование (или «не ебём мозг сразу»). Вот тут гениальность, блядь. Они не вываливают весь хлам на пол и не перебирают его за раз. Нет! Они делают это постепенно, под шумок.
- Каждый раз, когда ты что-то записываешь или удаляешь из мапы, она тебе под носом, пока ты не видишь, перекладывает вещи из одного-двух старых ящиков в новые.
- Когда ты читаешь, а ключ ещё в старом ящике, она тебя не посылает, а честно ищет и там, и в новом месте. Умная, сука, система!
- Финал. Когда весь хлам перетащен, старый шкаф выносят на помойку (сборщик мусора его прибирает). И ты живёшь уже с новым, просторным.
И главная, блядь, особенность, которую все забывают:
Сама по себе эта мапа — не потокобезопасная тётка. Если к ней со всех сторон полезет толпа горутин — одна будет писать, другая читать, третья удалять — получится пиздец, бардак и паника. Она не умеет с этим сама. Поэтому либо ты ставишь охранника — sync.RWMutex (чтоб не все сразу), либо идёшь в специальный магазин за готовым шкафом с замком — sync.Map. А иначе — сам дурак, чувак. Всё развалится, как карточный домик.