Что означает ошибка OOM
OOM Killer (Out-of-Memory Killer) — это механизм ядра Linux, который автоматически завершает один или несколько процессов, когда система исчерпала доступную оперативную память (RAM) и swap-пространство. Это не ошибка приложения, а реакция ядра на критическую нехватку памяти, чтобы избежать полного зависания системы.
Типичный вывод в логах (dmesg):
[12345.678901] Out of memory: Kill process 1234 (some-process) score 999 or sacrifice child
[12345.678912] Killed process 1234 (some-process) total-vm:123456kB, anon-rss:98765kB, file-rss:1234kB
Симптомы у пользователя: неожиданное падение сервисов (например, PostgreSQL, Java-приложение, веб-сервер), сообщения Killed в терминале при запуске программ, невозможность создать новые процессы.
Причины возникновения
- Физическая нехватка RAM + swap. Запущенные процессы потребляют больше памяти, чем доступно физически + в swap. Часто происходит из-за утечек памяти (memory leaks) в долгоживущих приложениях.
- Агрессивный overcommit. Ядро Linux по умолчанию разрешает выделение памяти, которой физически нет (overcommit), полагаясь на то, что приложения её не используют полностью. Если же все процессы решат использовать зарезервированное — OOM Killer сработает.
- Конфигурация swap слишком мала или отсутствует. На системах без swap или с минимальным swap (например, 1-2 ГБ) OOM Killer срабатывает при меньшей нагрузке, так как нет буфера для выгрузки неактивных страниц.
- Злонамеренный или buggy процесс. Процесс может намеренно выделять память (например, fork-бомба) или содержать баг, приводящий к бесконечному росту потребления памяти.
- Недостаток cgroup-лимитов. В контейнерах (Docker) или виртуальных средах, где память ограничена через cgroups, OOM Killer может убить процесс внутри контейнера, даже если на хосте ещё есть свободная память.
Способы решения
Способ 1: Быстрая диагностика и освобождение памяти
Первым делом определите, какой процесс виновник и сколько памяти свободно.
- Проверьте общую статистику:
free -h
Обратите внимание на строкуavailable— это реально доступная память для новых процессов. Если значение близко к нулю — проблема серьёзная. - Найдите "тяжёлых" процессов:
ps aux --sort=-%mem | head -10
Это покажет топ-10 процессов по потреблению RAM. - Проверьте, нет ли процессов с аномально высоким
VIRT(виртуальная память), но низкимRES(физическая) — возможна overcommit-атака. - Если система ещё реагирует, попробуйте вручную завершить не критичный процесс с высоким потреблением:
sudo kill -9 <PID>
Используйте с осторожностью, только если уверены в природе процесса.
Способ 2: Добавление или увеличение swap-файла
Если у вас мало swap (например, менее размера RAM), создайте дополнительный swap-файл. Это даст ядру больше "воздуха".
- Создайте файл размером 4 ГБ (подберите размер под нагрузку):
sudo fallocate -l 4G /swapfile
Еслиfallocateнедоступен, используйтеdd if=/dev/zero of=/swapfile bs=1M count=4096. - Установите правильные права и подготовьте как swap:
sudo chmod 600 /swapfile sudo mkswap /swapfile - Активируйте swap:
sudo swapon /swapfile - Чтобы swap включался автоматически при загрузке, добавьте строку в
/etc/fstab:echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab - Проверьте:
swapon --show free -h
⚠️ Важно: Swap на SSD ускорит работу, но увеличит износ. Для серверов с большим RAM (16+ ГБ) swap может быть минимальным (2-4 ГБ) или отключённым, но тогда OOM Killer сработает быстрее.
Способ 3: Настройка OOM-политики для конкретных процессов
Иногда нужно защитить критичный сервис (например, базу данных) от убийства, пожертвовав менее важным.
Для системd-сервисов (современные дистрибутивы)
Создайте override-конфигурацию для сервиса:
sudo systemctl edit <имя_сервиса>
В открывшемся редакторе добавьте:
[Service]
OOMScoreAdjust=-900
Значение -1000 — абсолютная защита (ядро не будет убивать такой процесс даже при тотальном OOM). -900 — очень низкий приоритет на убийство. Сохраните и перезапустите сервис:
sudo systemctl daemon-reload
sudo systemctl restart <имя_сервиса>
Для ручных процессов
Запустите процесс с изменённым OOM-скорингом через systemd-run (если система использует systemd):
systemd-run --scope -p OOMScoreAdjust=-500 ./ваша_программа
Или через prctl в коде (для разработчиков). Для уже запущенного процесса изменить oom_score_adj можно:
echo -1000 | sudo tee /proc/<PID>/oom_score_adj
Требует прав root, изменение временное (до перезапуска процесса).
Способ 4: Тюнинг ядра — overcommit и watermark
Эти настройки глобальные и требуют понимания. Редактируйте /etc/sysctl.conf или создайте файл в /etc/sysctl.d/.
- Уменьшите агрессивность overcommit (по умолчанию
0— heuristic overcommit,1— всегда overcommit,2— строгий режим):sudo sysctl -w vm.overcommit_memory=2 sudo sysctl -w vm.overcommit_ratio=80 # Разрешить выделять не более 80% RAM+swap
В режиме2ядро будет отклонять запросы наmalloc(), если не хватит памяти, что предотвратит OOM, но может сломать некоторые приложения, ожидающие overcommit. - Настройте watermark'ы — пороги, при которых ядро начинает активный OOM Killer:
sudo sysctl -w vm.min_free_kbytes=65536 # Минимальный резерв свободной памяти (в КБ). Увеличьте, если OOM срабатывает при наличии памяти.
Будьте осторожны: слишком высокое значение может привести к неэффективному использованию памяти. - Внесите изменения постоянно:
echo "vm.overcommit_memory=2" | sudo tee -a /etc/sysctl.conf echo "vm.overcommit_ratio=80" | sudo tee -a /etc/sysctl.conf
Способ 5: Анализ и исправление утечек памяти
Если OOM Killer периодически убивает один и тот же процесс (например, java или python), вероятна утечка.
- Мониторинг в реальном времени:
watch -n 1 'ps aux --sort=-%mem | head -5' - Для Java-приложений используйте
jcmdилиjmapдля анализа heap:jcmd <PID> GC.heap_info jmap -dump:live,format=b,file=heap.hprof <PID>
Затем проанализируйте дамп через MAT (Memory Analyzer Tool). - Для процессов на C/C++ используйте
valgrind --tool=memcheckпри тестировании, илиpmap -x <PID>для просмотра детального распределения памяти. - Проверьте логи приложения на предмет ошибок, связанных с памятью (например,
OutOfMemoryErrorв Java).
Профилактика
- Регулярный мониторинг. Настройте алерты (через Prometheus+Grafana, Zabbix, Netdata) на ключевые метрики:
mem.available,vmstat.si/so,oom_kill(счётчик в/proc/vmstat). - Адекватный swap. Размер swap рекомендуется как минимум равен размеру RAM для настольных систем, для серверов — от 1/2 до 1× RAM в зависимости от нагрузки.
- Ограничение ресурсов через cgroups. Для контейнеров и сервисов явно задавайте лимиты памяти (
memory.limit_in_bytesв Docker илиMemoryMaxв systemd), чтобы OOM Killer срабатывал внутри изолированной группы, а не на всём хосте. - Обновление ПО. Утечки памяти часто исправляются в обновлениях. Следите за changelog критичных приложений (базы данных, веб-серверы, виртуализация).
- Настройка
oom_score_adjдля важных сервисов. Как показано в Способе 3, защитите ключевые процессы от автоматического убийства. - Планирование нагрузки. Зная пиковые нагрузки, рассчитайте необходимый объём RAM + swap. Используйте
stress-ngилиmemtesterдля тестирования стабильности под давлением.