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

Где находится экземпляр структуры, созданной через Make?

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

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

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

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

Расположение экземпляра структуры, созданной через make

Чтобы дать точный ответ, важно сразу прояснить терминологию, так как вопрос содержит техническую неточность. В языке Go не существует возможности создать экземпляр структуры с помощью встроенной функции make. Функция make в Go предназначена исключительно для инициализации и создания встроенных (built-in) ссылочных типов: срезов (slices), карт (maps) и каналов (channels). Для создания экземпляра структуры (как и любого другого типа) используется встроенная функция new или, что гораздо чаще, составной литерал (composite literal) с фигурными скобками {}.

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

Создание экземпляров структур

  1. Использование составного литерала (Type{...}):
    Это самый распространенный способ. Объект создается и инициализируется в месте объявления.
```go
type Person struct {
    Name string
    Age  int
}

func main() {
    // Экземпляр структуры создается с помощью составного литерала
    p1 := Person{Name: "Alice", Age: 30}
    fmt.Println(p1)
}
```
    **Расположение в памяти:** Переменная `p1` является **значением типа `Person`**. Если она объявлена внутри функции (как в примере), то сам экземпляр структуры (ее поля `Name` и `Age`) располагается в **стеке (stack)** горутины. Однако это справедливо только до тех пор, пока компилятор Go может доказать, что указатель на эту структуру не "убегает" (escape) из функции. В противном случае, для обеспечения корректного времени жизни, компилятор перемещает (**"утекает"**, escapes) экземпляр в **кучу (heap)**. Это решение принимается на этапе компиляции с помощью **анализа побега (escape analysis)**.

  1. Использование функции new:
    Функция `new(T)` выделяет память для типа `T`, **заполняет ее нулевыми значениями (zeroed memory)** и возвращает **указатель `*T`** на эту область.
```go
func main() {
    // new возвращает УКАЗАТЕЛЬ на структуру
    p2 := new(Person) // p2 имеет тип *Person
    p2.Name = "Bob"   // Работаем через указатель
    fmt.Println(p2)
}
```
    **Расположение в памяти:** Аналогично предыдущему случаю. Память для структуры может быть выделена как в стеке, так и в куче, в зависимости от результатов escape-анализа. Функция `new` сама по себе не предписывает конкретное место.

Для чего тогда используется make?

Функция make создает и инициализирует объекты встроенных ссылочных типов, возвращая не указатель, а готовое к использованию значение этого типа.

  1. Для срезов (slice): make аллоцирует базовый массив и возвращает срез, который является дескриптором (указатель на массив + длина + емкость).

    // Создает срез типа []int длиной 5 и емкостью 10.
    // Базовый массив размещается в куче.
    s := make([]int, 5, 10)
    
  2. Для карт (map): make создает и инициализирует хэш-таблицу, готовую для вставки пар ключ-значение.

    // Создает готовую к использованию карту.
    // Внутренняя структура хэш-таблицы размещается в куче.
    m := make(map[string]int)
    
  3. Для каналов (channel): make создает и инициализирует буферизованный или небуферизованный канал.

    // Создает небуферизованный канал для целых чисел.
    // Внутренняя структура данных канала размещается в куче.
    ch := make(chan int)
    

Ключевое отличие от new:

  • new(T) возвращает *T (указатель), инициализированный нулями.
  • make(T, args...) возвращает готовое значение типа T, которое является сложной структурой данных (дескриптором), ссылающейся на данные в куче. Например, new([]int) вернет nil-указатель на срез, в то время как make([]int, 0) вернет пустой, но готовый к использованию срез.

Итог: Где же находится объект?

Для структур, созданных через литерал или new, и для внутренних данных типов, созданных через make, ответ зависит от анализа побега (escape analysis) компилятора Go.

  • Стек: Если компилятор может доказать, что ссылка на объект не переживает функцию, в которой он создан, объект размещается в стеке текущей горутины. Это высокоскоростная операция.
  • Куча: Если ссылка на объект "убегает" из функции (например, возвращается как значение, передается в глобальную переменную, отправляется в канал, или на него ссылается значение, которое уже находится в куче), то компилятор размещает объект в куче. Управление памятью в куче осуществляется сборщиком мусора (garbage collector).

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

Вывод: Вопрос, сформулированный как "экземпляр структуры, созданной через make", корректен только применительно к внутренним объектам срезов, карт и каналов. Эти внутренние данные практически всегда находятся в куче. Для создания же экземпляров пользовательских структур функция make не применяется.