Топ-3 вопроса на собеседованиях по Go
Ниже — три темы, которые встречаются на мидл и синьор собеседованиях чаще всего. Не потому что они сложные, а потому что по ответу на них сразу видно: человек понимает рантайм изнутри или нет.
1. Что такое GMP
GMP — это модель планировщика горутин в рантайме Go. Три компонента:
- G (Goroutine) — горутина: единица исполнения с собственным стеком и состоянием.
- M (Machine) — поток операционной системы, на котором физически выполняется код.
- P (Processor) — логический процессор с локальной очередью горутин. Является посредником между G и M.
Аналогия через железо: представь обычный компьютер. У тебя есть задачи (G), ядра CPU (P — их количество задаётся через GOMAXPROCS, по умолчанию равно числу аппаратных ядер) и системные потоки (M — рабочие исполнители). Ни одна горутина не попадает напрямую на ядро процессора — она сначала встаёт в очередь к P, затем P передаёт её потоку M, и только M исполняет её на железе.
Ключевое следствие этой архитектуры: если горутина блокируется на системном вызове (например, ждёт ответа от базы данных), M не простаивает — P немедленно берёт следующую G из очереди. Если локальная очередь одного P опустела, он заимствует горутины у других P — это механизм work stealing. Именно поэтому в Go можно запускать сотни тысяч горутин при восьми ядрах и небольшом числе системных потоков, не получая деградации производительности.
Рекомендую видео: Николай Тузов — "Планировщик Go: самый подробный гайд простым языком"
2. Что такое context и зачем он нужен
context.Context — это интерфейс, предоставляющий механизм управления жизненным циклом операции: передачу сигналов отмены, дедлайнов, таймаутов и request-scoped значений между горутинами.
Практическая задача: пользователь отправил HTTP-запрос, который запускает три параллельных запроса к базам данных. Через секунду клиент разорвал соединение. Без контекста все три запроса продолжат выполняться и расходовать ресурсы. Контекст позволяет отменить всё дерево дочерних операций одним вызовом.
Основные конструкторы:
- context.Background() — корневой контекст, отправная точка дерева контекстов.
- context.WithCancel(ctx) — возвращает дочерний контекст и функцию cancel() для ручной отмены.
- context.WithTimeout(ctx, d) — автоматически отменяется по истечении таймаута.
- context.WithDeadline(ctx, t) — отменяется в конкретный момент времени.
- context.WithValue(ctx, key, val) — передача данных вниз по цепочке вызовов.
Горутина проверяет завершение через <-ctx.Done(). По соглашению, принятому в стандартной библиотеке Go, контекст передаётся первым аргументом функции: func DoSomething(ctx context.Context, ...). Это не синтаксическое требование языка, а архитектурное соглашение — его нарушение считается ошибкой дизайна.
3. Heap и Stack — где живут переменные
В Go у каждой горутины есть собственный стек, а куча (heap) — общая для всей программы и управляется сборщиком мусора.
Stack (стек):
Работает по принципу LIFO. Хранит локальные переменные функции, аргументы и адреса возврата. Выделение и освобождение памяти на стеке — это сдвиг указателя стека, что делает его принципиально быстрее кучи. После завершения функции её фрейм стека уничтожается автоматически, без участия GC. Начальный размер стека горутины в Go — 2 КБ, однако стек умеет динамически расти по мере необходимости.
Heap (куча):
Хранит объекты, время жизни которых неизвестно на этапе компиляции. Операции с кучей медленнее из-за динамического менеджмента памяти и давления на GC — каждый объект в куче в конечном счёте потребует работы сборщика мусора.
Кто принимает решение? Компилятор Go выполняет escape analysis — статический анализ того, "убегает" ли переменная за пределы своей функции:
- Переменная не покидает функцию → размещается на стеке.
- Переменная возвращается как указатель, захватывается замыканием или горутиной → уходит на heap.
- Объект слишком большой для стека → heap.
Увидеть результат escape analysis можно командой:
go build -gcflags="-m" ./...
Компилятор явно укажет, какие переменные ушли на heap и почему. Это полезный инструмент при профилировании аллокаций.
Итог: стек — быстро, детерминированно, без GC. Heap — гибко, но с ценой в виде давления на сборщик мусора. Понимание escape analysis позволяет осознанно снижать число аллокаций в горячих путях.