🧠
Хостинг

Нехватка памяти (OOM): почему завершаются процессы PHP и Python

09.11.2026
← Все статьи

Запущенный ночью скрипт импорта утром оказывается остановленным на полпути, а в логе всего одно слово: Killed. Или страница большого отчёта в интернет-магазине вместо открытия отдаёт ошибку 500. Причина у этих ситуаций часто общая: процесс запросил больше оперативной памяти, чем ему разрешено, и система принудительно его остановила. Это называется OOM, то есть «out of memory». В этой статье разберём, как возникает OOM, как отличить его от других ошибок и как сделать код экономным по памяти.

Два разных лимита памяти

На хостинге есть два отдельных ограничения, связанных с памятью, и путаница между ними отнимает много времени. Первое — собственная настройка PHP memory_limit. Она определяет, сколько памяти может использовать один PHP-скрипт. Если скрипт её превышает, PHP сам прекращает работу и пишет в лог понятное сообщение:

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted
(tried to allocate 20480 bytes) in /var/www/USER/data/www/example.uz/report.php on line 42

В сообщении указаны файл и строка, на которой закончилась память, поэтому найти причину сравнительно легко. 134217728 байт равны 128 мегабайтам, то есть скрипт упёрся именно в этот предел.

Второй лимит — RAM, выделенная всему аккаунту. В нём суммируется память всех процессов: PHP, Python, Node.js и прочих. Если общий объём превышает лимит, операционная система без предупреждения завершает один из процессов, обычно самый «тяжёлый». Программа при этом не успевает записать ошибку: в терминале появляется слово Killed, а веб-страница завершается ошибкой 500 или 503. Именно это и называют OOM kill.

Как распознать OOM

Некоторые признаки явно указывают на OOM. Скрипт на Python или Node.js бесследно останавливается посреди работы, а код выхода равен 137. Этот код означает, что процесс был убит извне сигналом SIGKILL. Долго работающий бот или приложение время от времени сами перезапускаются. Сайт обычно работает нормально, но выдаёт ошибку при крупном экспорте, загрузке изображений или тяжёлом отчёте. А на графике ресурсов в панели хостинга линия памяти в эти минуты касается верхней границы.

Следующие команды помогают понять, какой процесс сколько памяти занимает:

# Ваши процессы, отсортированные по памяти (RSS, в килобайтах)
ps -u "$USER" -o pid,rss,etime,cmd --sort=-rss | head

# Сколько памяти скрипт занял на пике
/usr/bin/time -v python3 import_orders.py 2>&1 | grep "Maximum resident"

# Если команда завершилась с "Killed", код выхода будет 137
echo $?

Команда ps сортирует процессы по объёму памяти. /usr/bin/time -v показывает пиковое потребление памяти за время работы скрипта, и эту цифру можно сравнить с лимитом RAM в вашем тарифе.

Почему заканчивается память: типичные причины

Загрузка всех данных разом

Самая частая ошибка — прочитать большой файл или всю таблицу базы в память за один раз. Excel-файл на сто тысяч строк или таблица заказов в памяти занимают в несколько раз больше исходного объёма, потому что каждое значение внутри программы превращается в отдельный объект.

Обработка больших изображений

Фото с камеры телефона на диске весит несколько мегабайт, но при открытии для изменения размера оно записывается в память в несжатом виде и занимает десятки мегабайт. Если одновременно загружается несколько таких фото, память быстро заканчивается.

Слишком много рабочих процессов

При запуске Python-приложений через Gunicorn или похожие серверы каждый рабочий процесс (worker) держит в памяти полную копию приложения. Четыре воркера — это вчетверо больше памяти. Для виртуального хостинга или небольшого VPS число воркеров часто задают с запасом.

Утечка памяти

Иногда программа не освобождает использованную память: например, при каждом запросе в глобальный список добавляется элемент, который никогда не удаляется. Такое приложение несколько часов работает нормально, а затем постепенно заполняет память и останавливается по OOM.

Пример: бот ресторана в Бухаре

Ресторан в Бухаре использовал для заказов Telegram-бота на Python. Каждый вечер администратор загружал в бота месячный отчёт из Excel, а бот анализировал его и записывал в базу. По мере роста файла бот стал останавливаться каждый раз в этот момент, а в логе оставалось только Killed. Проверка показала, что файл целиком открывался в памяти, затем все строки сначала собирались в список и лишь потом записывались в базу. Код переписали на построчную обработку:

from openpyxl import load_workbook

# read_only=True не загружает файл в память целиком
wb = load_workbook("buyurtmalar.xlsx", read_only=True)
ws = wb.active
for row in ws.iter_rows(min_row=2, values_only=True):
    save_order(row)          # каждую строку сразу пишем в базу
wb.close()

После этого пиковое потребление памяти резко снизилось, и бот перестал падать. Проблему решил не более мощный тариф, а изменение пары строк кода.

Способы экономии памяти

В PHP крупные выгрузки и отчёты эффективнее всего выполнять порциями. Вместо выборки всей таблицы одним запросом обработка по частям держит потребление памяти стабильным:

// Вместо выборки всего сразу обрабатываем порциями по 500
$offset = 0;
do {
    $rows = $db->query("SELECT id, name, total FROM orders ORDER BY id LIMIT 500 OFFSET $offset")->fetchAll();
    foreach ($rows as $r) { fputcsv($out, $r); }
    $offset += 500;
} while (count($rows) === 500);

Для веб-приложений на Python подберите число воркеров под реальную нагрузку, а при утечках памяти настройте их автоматическое обновление после определённого числа запросов:

gunicorn app:app --workers 2 --max-requests 500 --max-requests-jitter 50

Эта настройка пересоздаёт каждый воркер примерно после 500 запросов, и утёкшая память освобождается. Помните, что это временное решение: настоящую причину утечки всё равно нужно найти. Для изображений ограничьте максимальный размер при загрузке и обрабатывайте их по очереди, по одному.

Повышение memory_limit тоже иногда оправдано, например если в админке WordPress работает тяжёлый плагин. Но так поднимается только предел PHP, а не общий лимит RAM аккаунта. Если исчерпывается именно лимит аккаунта, увеличение memory_limit может даже ускорить OOM kill.

Когда нужно больше RAM

Если код оптимизирован, число воркеров подобрано и утечек нет, а приложение всё равно работает на пределе, ему действительно нужно больше памяти. Для постоянно работающих ботов и приложений на Node.js или Python часто удобнее VPS: там память принадлежит только вам, и процессами вы управляете сами. В кабинете sayt.uz можно сменить тариф хостинга или заказать VPS, а с вопросами по графикам ресурсов обращайтесь в техподдержку sayt.uz через тикет.

Похожие статьи

🤖 Python-бот 24/7: запуск через systemd и PM2 🧠 «Allowed memory size exhausted»: ошибка памяти PHP и как её правильно решить 🚀 Полностраничный кеш: когда он полезен, а когда мешает ⚡ Как развернуть FastAPI на сервере: Uvicorn, systemd и Nginx
🌐 Язык
🇺🇿 O'zbek 🇺🇿 Ўзбек 🇷🇺 Русский ✓ 🇬🇧 English