← Назад к вопросам

Что такое capacity?

2.0 Middle🔥 184 комментариев
#Основы Go

Комментарии (4)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Capacity (Вместимость) в Go?

Capacity (вместимость, ёмкость) — это фундаментальное свойство срезов (slices) и буферов каналов (channels) в языке Go. Оно определяет максимальное количество элементов, которое может вместить внутренний массив среза или буфер канала без необходимости перевыделения памяти (reallocation).

Capacity в контексте срезов (Slices)

Срез в Go — это дескриптор, состоящий из трёх компонентов: указателя на базовый массив (underlying array), длины (length) и вместимости (capacity).

// Структура среза (упрощённо)
type sliceHeader struct {
    Pointer uintptr // Указатель на элемент массива
    Len     int     // Текущая длина (length)
    Cap     int     // Ёмкость (capacity)
}

Ключевые аспекты Capacity для срезов:

  1. Отношение к базовому массиву: Capacity — это общее количество элементов в базовом массиве, начиная с первого элемента среза. Это не ёмкость всего массива с его начала.

    arr := [5]int{10, 20, 30, 40, 50} // Базовый массив capacity = 5
    sl := arr[1:4]                     // Срез [20, 30, 40]
    fmt.Println(len(sl), cap(sl))      // Длина: 3, Capacity: 4
    // Capacity = 4, потому что в массиве осталось место: элементы [40] и [50]
    
  2. Динамическое увеличение (реаллокация): При добавлении элементов в срез с помощью append сверх его текущей длины, но в пределах capacity, новые элементы помещаются в свободное место базового массива, и длина увеличивается. Capacity остаётся прежней.

    sl := []int{1, 2, 3, 4}
    fmt.Println(cap(sl)) // capacity = 4
    sl = append(sl, 5)
    // Если capacity было 4, а мы добавляем 5-й элемент,
    // происходит реаллокация: создаётся новый массив большего размера,
    // элементы копируются, capacity увеличивается (часто по формуле 2x от старого).
    fmt.Println(cap(sl)) // capacity станет, например, 8
    
  3. Управление производительностью: Понимание capacity критично для оптимизации. Частые реаллокации из-за append к небольшим срезам в цикле могут стать узким местом. В таких случаях используют предварительное выделение (pre-allocation) с помощью make, задавая начальную capacity.

    // Неоптимально: может быть несколько реаллокаций
    var sl []int
    for i := 0; i < 1000; i++ {
        sl = append(sl, i)
    }
    
    // Оптимально: одна аллокация, capacity = 1000
    sl := make([]int, 0, 1000) // length=0, capacity=1000
    for i := 0; i < 1000; i++ {
        sl = append(sl, i)
    }
    
  4. Функции для работы с capacity:

    *   `cap(slice)` — возвращает текущую вместимость среза.
    *   `make([]T, length, capacity)` — создаёт срез с предопределённой длиной и вместимостью.

Capacity в контексте каналов (Channels)

Для буферизованных каналов capacity определяет размер буфера — количество элементов, которые могут быть отправлены в канал без блокировки отправителя, пока получатель их не прочитал.

ch := make(chan int, 5) // Канал с буфером capacity = 5
ch <- 1 // Не блокируется
ch <- 2 // Не блокируется
// ...
ch <- 5 // Не блокируется
ch <- 6 // Блокировка! Ждёт, пока из канала не будет прочитан хотя бы один элемент.

Ключевые аспекты Capacity для каналов:

  • Канал с capacity = 0 (или созданный make(chan int)) является небуферизованным. Отправка и получение в нём синхронизированы — операции блокируются до тех пор, пока обе стороны не будут готовы (rendezvous).
  • Канал с capacity > 0буферизованный. Отправка блокируется только когда буфер полон, а получение — только когда буфер пуст.
  • Capacity канала задаётся при создании функцией make и не может быть изменена в дальнейшем.
  • Узнать capacity канала можно с помощью встроенной функции cap(ch).

Почему Capacity — это важно?

  1. Производительность: Контроль над capacity срезов позволяет минимизировать дорогостоящие операции выделения и копирования памяти, что напрямую влияет на скорость выполнения программ, особенно при обработке больших объёмов данных.
  2. Предсказуемость поведения: Понимание capacity буфера канала помогает проектировать корректные параллельные взаимодействия, избегая deadlock'ов и неожиданных блокировок.
  3. Эффективное использование памяти: Слишком большой capacity может привести к излишнему потреблению памяти, слишком маленький — к частым реаллокациям. Важно находить баланс.

Итог: Capacity — это не просто техническая деталь, а инструмент управления памятью и производительностью. Грамотное использование capacity (предварительное выделение для срезов, выбор размера буфера для каналов) является признаком опытного Go-разработчика и позволяет создавать эффективные и надёжные приложения.

Что такое capacity? | PrepBro