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

Для каких типов память не будет выделяться в куче

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

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

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

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

Память без выделения в куче в Go

В Go есть несколько типов, для которых память никогда выделяется в куче (heap) — она всегда размещается только в стеке (stack) или является частью самой программы. Эти типы можно разделить на несколько категорий.

1. Базовые типы с фиксированным размером

Все базовые численные типы, булевы и символы (byte/rune) размещаются в стеке, если используются локально:

  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64, uintptr
  • float32, float64
  • complex64, complex128
  • bool
  • byte (alias for uint8), rune (alias for int32)

Пример локальной переменной, которая будет в стеке:

func calculate() {
    x := 42          // int - в стеке
    y := 3.14        // float64 - в стеке
    flag := true     // bool - в стеке
}

2. Массивы с фиксированным размером

Массивы (array) с предопределённой длиной, если они локальные и не слишком большие, также размещаются в стеке:

func process() {
    var smallArray [10]int        // 10 элементов int - в стеке
    coordinates := [3]float64{1.0, 2.0, 3.0} // в стеке
}

Важно: если массив очень большой или используется в контексте, который требует escape analysis для heap, он может быть перемещён в кучу.

3. Структуры (struct) без указателей

Локальные структуры, которые не содержат ссылок на heap-объекты (например, указатели, slices, maps, функции), обычно остаются в стеке:

type Point struct {
    X, Y float64
}

func createPoint() {
    p := Point{X: 10, Y: 20}  // Структура полностью в стеке
}

4. Указатели на стековые объекты

Само значение указателя может быть в стеке, если он ссылается на другой стековый объект:

func pointerExample() {
    value := 100          // int в стеке
    ptr := &value         // Указатель *int тоже в стеке
}

5. Интерфейсы с стековыми значениями

Интерфейсные переменные могут хранить стековые значения, если тип значения удовлетворяет интерфейсу и не требует escape:

type Printer interface {
    Print()
}

type ConsolePrinter struct{}

func (cp ConsolePrinter) Print() {
    fmt.Println("Printing")
}

func useInterface() {
    var printer Printer = ConsolePrinter{}  // Интерфейс хранит стековую структуру
}

6. Константы и глобальные переменные

Константы (const) вообще не имеют динамического выделения памяти — они существуют только на этапе компиляции.

Глобальные переменные размещаются в статической памяти (static/global data segment), не в куче:

const Version = "1.0"           // Константа - нет динамической памяти
var globalCounter int = 0       // Глобальная переменная - в статической области

7. Строковые литералы

Строковые литералы (string literals) хранятся в read-only data segment, не в куче:

func showMessage() {
    msg := "Hello, World!"  // Строковый литерал - в read-only сегменте
}

Когда стековые объекты могут попасть в кучу?

Escape analysis компилятора Go определяет, нужно ли перемещать объект в кучу. Критерии:

  • Если объект ссылается из функции после её завершения (возвращается, передаётся в глобальную область)
  • Если размер объекта слишком велик для стека
  • Если объект используется одновременно несколькими goroutines
  • Если объект хранится в типе, который всегда аллоцируется в куче (например, slice, map, chan)

Пример escape в кучу:

func escapeExample() *int {
    x := 42  // Локальный int
    return &x  // Указатель на x возвращается -> x escape в кучу
}

Выводы

Память не выделяется в куче для:

  1. Локальных базовых типов (числа, bool)
  2. Фиксированных массивов (если не escape)
  3. Структур без ссылочных полей (если локальные)
  4. Констант и глобальных переменных (статическая память)
  5. Строковых литералов (read-only сегмент)

Но ключевое правило в Go: компилятор через escape analysis сам решает, где размещать память. Программист не управляет этим напрямую, что уменьшает риск ошибок памяти.