Когда сборщик мусора (GC) освобождает память, занимаемую слайсом?

Ответ

Память, выделенная для слайса, освобождается сборщиком мусора (GC), когда на базовый (underlying) массив этого слайса больше не остается ссылок.

Сам по себе слайс — это легковесная структура (заголовок), состоящая из трех полей:

  • Указатель на начало базового массива.
  • Длина (length) — количество элементов в слайсе.
  • Ёмкость (capacity) — общее количество элементов в базовом массиве от его начала.

Сборщик мусора отслеживает достижимость объектов в памяти. Пока хотя бы один слайс (или любая другая переменная) ссылается на базовый массив, этот массив считается достижимым и не будет удален.

Проблема утечки памяти при сабслайсинге

Частая ошибка возникает, когда из большого слайса создается маленький подслайс. Даже если вы используете только маленький срез, он все равно удерживает в памяти весь исходный большой массив.

func getSmallSlice() []int {
    // Создаем большой массив и слайс на его основе
    bigSlice := make([]int, 1000000)
    // ... какая-то работа с bigSlice

    // Возвращаем маленький срез из первых 10 элементов
    smallSlice := bigSlice[:10]
    return smallSlice
}

// После вызова getSmallSlice() переменная bigSlice исчезнет,
// но возвращенный smallSlice все еще ссылается на тот же
// гигантский массив из 1,000,000 элементов. Память не освободится.

Как правильно освободить память?

  1. Обнуление ссылки: Если большой слайс больше не нужен, можно присвоить ему nil. Это разорвет ссылку, и если других ссылок на массив нет, GC сможет его собрать.

    bigSlice = nil // Теперь массив может быть собран
  2. Копирование (предпочтительный способ): Создайте новый слайс нужного размера и скопируйте в него только необходимые данные. Это гарантирует, что вы не будете удерживать лишнюю память.

    // Вместо return bigSlice[:10]
    result := make([]int, 10)
    copy(result, bigSlice[:10])
    return result // Возвращаем новый слайс с собственным компактным массивом