> Что происходит в программе при отсутствии доступной памяти (Go)
Уровень: middle · Роль: backend · Категория: Технические вопросы
Компании: Avito
Стек: Go
> Пример ответа
Короткий ответ
В Go при нехватке памяти runtime вызывает фатальную ошибку out of memory и аварийно завершает программу с кодом 2. Это происходит, когда аллокатор не может получить память у операционной системы через mmap или brk. В отличие от C, где malloc возвращает NULL, в Go нет возможности обработать OOM внутри программы - процесс убивается немедленно, без паники и без вызова defer.
Подробное объяснение
Go использует собственный аллокатор на основе tcmalloc. Когда программа запрашивает память, runtime сначала пытается удовлетворить запрос из уже зарезервированных страниц. Если свободных страниц нет, аллокатор обращается к ОС за новыми страницами через mmap.
Если ОС отказывает в выделении памяти (например, из-за лимита ulimit -v, cgroup limit или исчерпания физической памяти и swap), runtime вызывает runtime.throw("out of memory"). Это приводит к немедленному завершению процесса с сообщением в stderr и кодом выхода 2.
Важно: в Go нет механизма перехвата OOM. Даже если вы напишете recover() вокруг аллокации, это не поможет - OOM не является паникой, это фатальная ошибка runtime. Паника возникает только при panic(nil) или явном вызове, но не при нехватке памяти.
Также стоит учитывать, что в Go есть два уровня нехватки памяти:
- нехватка виртуальной памяти (адресного пространства) - редка на 64-битных системах;
- нехватка физической памяти - ОС может убить процесс через OOM-killer (SIGKILL), что тоже не перехватывается.
На практике
На практике OOM в Go чаще всего возникает из-за:
- бесконечного роста слайса или мапы без ограничений;
- утечки горутин, каждая из которых держит память;
- слишком большого
GOMAXPROCSпри ограниченных ресурсах контейнера; - неверной оценки размера данных (например, чтение всего файла в память).
Для предотвращения OOM используют:
- лимиты памяти в контейнерах (cgroup) - но это приводит к SIGKILL, а не к ошибке Go;
- профилирование с
pprofдля поиска утечек; - ограничение размера структур данных;
- использование
sync.Poolдля переиспользования объектов.
Важно: в Go нет try/catch для OOM, поэтому единственный способ - не допускать ситуацию, а не обрабатывать её.
Пример кода
GOpackage mainimport "fmt"func main() {// Бесконечный рост слайса - классический путь к OOMvar data []bytefor {data = append(data, make([]byte, 1024*1024)...) // 1 MB за итерациюfmt.Println("allocated:", len(data)/1024/1024, "MB")}}
Этот код будет работать, пока не упрётся в лимит памяти, после чего runtime выведет:
runtime: out of memory: cannot allocate 1048576-byte block (6291456 in use) fatal error: out of memory
Как отвечать на собеседовании
Начните с факта: в Go OOM - фатальная ошибка, процесс завершается. Затем объясните механизм: runtime запрашивает память у ОС, при отказе вызывает throw. Упомяните, что recover не работает, потому что это не паника. Приведите практический пример - бесконечный append. Если спросят про отличие от C - скажите, что в C malloc возвращает NULL, и программист может обработать это, а в Go такой возможности нет. Завершите тем, что профилактика - единственный способ борьбы: лимиты, профилирование, аккуратная работа с большими данными.
Что проверяет интервьюер
Интервьюер проверяет:
- понимание модели памяти Go и роли runtime;
- знание разницы между паникой и фатальной ошибкой;
- осведомлённость о поведении ОС при нехватке памяти (OOM-killer);
- практический опыт - как вы находили и исправляли утечки памяти;
- умение объяснить, почему
recoverне спасает при OOM.
Типичные ошибки
- утверждение, что OOM можно поймать через
recover- это неверно; - смешивание OOM с паникой из-за
panic("out of memory")- это разные вещи; - игнорирование роли ОС - на Linux процесс может быть убит через SIGKILL, и это не ошибка Go;
- утверждение, что Go автоматически освобождает память при нехватке - GC не спасает от OOM, если память реально нужна;
- отсутствие упоминания
pprofкак инструмента диагностики.
> Похожие задачи по backend
Что такое селективность индекса и почему не всегда индекс используется?
Как реализовать паттерн fan-out fan-in в Go с использованием горутин?
В чем различия между стеком и хипом в Go
Какова асимптотическая сложность доступа по ключу в хэш-таблице
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью