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

Что произойдёт если превысить capacity слайса?

1.2 Junior🔥 162 комментариев
#Основы Go#Производительность и оптимизация

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

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

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

Механизм работы слайса при превышении capacity

При превышении capacity (вместимости) слайса в Go происходит автоматическое перевыделение памяти - создается новый внутренний массив большего размера, в который копируются все существующие элементы. Этот процесс называется reallocation (перераспределение) или growing (рост).

Детальный процесс перевыделения

  1. Аллокация нового массива:

    // Исходный слайс
    slice := make([]int, 3, 5) // len=3, cap=5
    
    // При добавлении элемента, когда len == cap
    slice = append(slice, 10) // len=4, cap=5 - всё еще помещается
    slice = append(slice, 20) // len=5, cap=5 - достигли capacity
    slice = append(slice, 30) // len=6 - ПРЕВЫШЕНИЕ!
    // Здесь происходит перевыделение памяти
    
  2. Алгоритм роста capacity: Go использует определенную стратегию увеличения размера:

    • Если capacity меньше 1024 элементов - новый capacity удваивается
    • Если capacity 1024 или больше - новый capacity увеличивается на 25%
    • Точная логика может зависеть от реализации компилятора
    var s []int
    for i := 0; i < 10; i++ {
        fmt.Printf("len=%d cap=%d\n", len(s), cap(s))
        s = append(s, i)
    }
    // Вывод покажет: cap=0,1,2,4,4,8,8,8,8,16
    

Критические аспекты перевыделения

  1. Производительность:

    // НЕЭФФЕКТИВНО: множественные перевыделения
    var inefficient []int
    for i := 0; i < 1000000; i++ {
        inefficient = append(inefficient, i)
    }
    
    // ЭФФЕКТИВНО: предварительное выделение
    efficient := make([]int, 0, 1000000)
    for i := 0; i < 1000000; i++ {
        efficient = append(efficient, i)
    }
    
  2. Изменение адреса памяти: После перевыделения слайс ссылается на новый массив, что может повлиять на другие ссылки:

    original := []int{1, 2, 3}
    reference := original[:2] // ссылка на часть original
    
    original = append(original, 4, 5, 6) // превышение capacity
    // Теперь original ссылается на НОВЫЙ массив
    // reference продолжает ссылаться на СТАРЫЙ массив
    
  3. Поведение при конкурентном доступе: Перевыделение не является атомарной операцией, что создает проблемы в конкурентных сценариях:

    var data []int
    var wg sync.WaitGroup
    
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(idx int) {
            defer wg.Done()
            data = append(data, idx) // ГОНОЧНАЯ УСЛОВИЕ!
        }(i)
    }
    wg.Wait()
    // Результат непредсказуем: возможна потеря данных или паника
    

Практические рекомендации

  1. Предварительное выделение: Используйте make() с указанием capacity при известном ожидаемом размере:

    // Хорошая практика
    items := make([]string, 0, expectedCount)
    
  2. Копирование вместо реаллокации: Для больших слайсов иногда эффективнее создать новый:

    func resizeSlice(s []int, newSize int) []int {
        result := make([]int, newSize)
        copy(result, s)
        return result
    }
    
  3. Мониторинг производительности:

    // Бенчмарк для сравнения подходов
    func BenchmarkAppend(b *testing.B) {
        for i := 0; i < b.N; i++ {
            var s []int
            for j := 0; j < 1000; j++ {
                s = append(s, j)
            }
        }
    }
    

Особые случаи

  1. Слайсы с capacity 0:

    zeroCap := make([]int, 0)
    zeroCap = append(zeroCap, 1) // всегда вызывает перевыделение
    
  2. Множественные append:

    s := []int{1, 2}
    s = append(s, 3, 4, 5) // может вызвать несколько перевыделений
    
    // Лучше использовать variadic append
    toAdd := []int{3, 4, 5}
    s = append(s, toAdd...)
    

Вывод: Превышение capacity слайса приводит к дорогостоящей операции перевыделения памяти с копированием всех элементов. В высоконагруженных приложениях это может стать узким местом производительности, поэтому важно оценивать ожидаемый размер данных и использовать предварительное выделение памяти через параметр capacity в функции make().

Что произойдёт если превысить capacity слайса? | PrepBro