Ответ
Слайс (slice) в Go — это не сам массив, а легковесная структура-дескриптор, которая описывает непрерывный сегмент базового массива (underlying array). Эта структура состоит из трех полей:
- Указатель (pointer): Адрес первого элемента базового массива, доступного через слайс.
- Длина (length): Количество элементов в слайсе. Это то, что возвращает функция
len()
. - Ёмкость (capacity): Максимальное количество элементов, которое можно разместить в базовом массиве, начиная с указателя слайса, без необходимости новой аллокации. Это то, что возвращает функция
cap()
.
Процесс выделения памяти
Выделение памяти происходит в момент создания слайса и зависит от способа его инициализации:
Через
make()
:s := make([]T, length, capacity)
- В куче (heap) выделяется память под базовый массив размером
capacity * sizeof(T)
. - Создается структура-заголовок слайса, в которую записываются указатель на этот массив,
length
иcapacity
. - Оптимизация: Если компилятор с помощью анализа escape-последовательностей (escape analysis) докажет, что слайс не "сбежит" из функции, где он был создан, память под массив может быть выделена на стеке (stack), что гораздо быстрее.
- В куче (heap) выделяется память под базовый массив размером
Через литерал:
s := []T{1, 2, 3}
- Происходит то же самое: создается базовый массив (в данном случае размером 3), и слайс указывает на него.
length
иcapacity
будут равны 3.
- Происходит то же самое: создается базовый массив (в данном случае размером 3), и слайс указывает на него.
При создании слайса из существующего массива или слайса:
s2 := s[1:3]
- Новый массив не создается! Создается только новый заголовок слайса (
s2
), который указывает на тот же самый базовый массив, что иs
, но с другим смещением, длиной и ёмкостью. Это делает операцию очень быстрой.
- Новый массив не создается! Создается только новый заголовок слайса (
Рост слайса при append
Когда вы добавляете элементы в слайс с помощью append
, и его length
превышает capacity
, происходит следующее:
- Аллокация нового, большего массива. Стратегия роста зависит от текущего размера:
- Для слайсов до 1024 элементов новая ёмкость обычно удваивается (
new_cap = old_cap * 2
). - Для больших слайсов рост замедляется, и новая ёмкость увеличивается примерно в 1.25 раза (
new_cap = old_cap * 1.25
). Это предотвращает слишком быстрый расход памяти.
- Для слайсов до 1024 элементов новая ёмкость обычно удваивается (
- Копирование всех элементов из старого массива в новый.
- Обновление указателя в заголовке слайса, чтобы он указывал на новый массив.
length
иcapacity
также обновляются.
Из-за этого append
может быть дорогой операцией, если вызывает переаллокацию. Поэтому, если вы заранее знаете примерный размер слайса, лучше сразу задавать capacity
через make
.