> Что происходит если памяти не хватает (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: Avito

Стек: Go

> Пример ответа

Короткий ответ

В Go нехватка памяти приводит к панике out of memory (OOM), которую нельзя перехватить через recover. Runtime пытается выделить память через mmap, и при отказе ОС процесс завершается с кодом 2 и сообщением fatal error: out of memory. Для контейнеров и Linux это часто означает убийство процесса OOM-killer'ом. Основные стратегии - ограничение потребления через GOMEMLIMIT, пулы объектов, оптимизация аллокаций и мониторинг.

Подробное объяснение

Go runtime использует собственный аллокатор на основе tcmalloc. При нехватке виртуальной памяти происходит следующее:

  1. Аллокатор пытается получить память у ОС через mmap или sbrk.
  2. Если ОС отказывает (нет свободной физической памяти или превышен лимит cgroup), runtime вызывает throw("out of memory").
  3. Это фатальная ошибка - процесс завершается без возможности восстановления, стектрейс печатается в stderr.

Важные нюансы:

  • Overcommit на Linux: ОС может разрешить mmap даже при нехватке физической памяти, но при фактическом обращении к страницам OOM-killer убьёт процесс.
  • GOMEMLIMIT (Go 1.19+): мягкий лимит на использование памяти, runtime начинает агрессивнее работать с GC, но не гарантирует защиту от OOM.
  • GODEBUG=madvdontneed=1 влияет на возврат памяти ОС.
  • Паника runtime: out of memory не перехватывается recover, так как это не паника в обычном смысле, а фатальная ошибка runtime.

На практике

Для backend-сервисов на Go:

  • Устанавливайте GOMEMLIMIT примерно на 80-90% от лимита контейнера.
  • Используйте pprof для профилирования heap-allocations.
  • Следите за метриками go_memstats_alloc_bytes и go_memstats_heap_inuse.
  • Применяйте пулы объектов (sync.Pool) для часто создаваемых структур.
  • Избегайте утечек горутин - каждая горутина имеет минимальный стек 2KB, при большом количестве это существенно.
  • Для работы с большими данными используйте streaming вместо загрузки всего в память.
  • Настраивайте GOGC - по умолчанию 100, увеличение снижает частоту GC, но повышает потребление памяти.

Пример кода

GO
// Пример использования sync.Pool для снижения аллокаций
var pool = sync.Pool{
New: func() interface{} {
return &bytes.Buffer{}
},
}
func process(data []byte) {
buf := pool.Get().(*bytes.Buffer)
defer pool.Put(buf)
buf.Reset()
buf.Write(data)
// работа с buf
}
// Пример установки GOMEMLIMIT в коде
func init() {
// 500 MB мягкий лимит
debug.SetMemoryLimit(500 << 20)
}

Как отвечать на собеседовании

Начните с того, что OOM в Go - это фатальная ошибка runtime, а не перехватываемая паника. Затем объясните механизм: аллокатор запрашивает память у ОС, при отказе - throw. Упомяните разницу между виртуальной и физической памятью, overcommit и OOM-killer. Перейдите к практическим мерам: GOMEMLIMIT, профилирование, пулы объектов. Если спросят про контейнеры - расскажите про cgroup limits и почему процесс может быть убит даже без явной ошибки Go. Хорошо показать понимание trade-off между GOGC и потреблением памяти.

Что проверяет интервьюер

  • Понимание устройства Go runtime и аллокатора.
  • Знание отличий между паникой и фатальной ошибкой.
  • Практический опыт борьбы с OOM в production.
  • Понимание работы с памятью в контейнерах и лимитами cgroup.
  • Умение рассуждать о trade-off между скоростью GC и потреблением памяти.

Типичные ошибки

  • Утверждение, что OOM можно поймать через recover - это неверно.
  • Игнорирование GOMEMLIMIT - многие не знают о нём даже на senior-позиции.
  • Путаница между heap memory и stack memory.
  • Предложение увеличить память как единственное решение - интервьюер ждёт анализ причин.
  • Незнание, что GODEBUG переменные могут влиять на поведение аллокатора.
  • Отсутствие упоминания мониторинга и профилирования как первой линии защиты.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью