TGViewer
PHP Fart Time PHP Fart Time @php_fart · 1.95K subscribers
Post #291 3.11K
#Article
💀 Как один ORM-запрос съел 300 МБ навсегда

История из нашего проекта: неоптимальный запрос через ORM загрузил ~100К записей из базы. Worker в RoadRunner вырос с 80 до ~500 МБ... и больше никогда не вернулся обратно.

Звучит как утечка памяти? Спойлер: это не баг, а особенность архитектуры PHP.

💡 Неочевидный факт: PHP никогда не возвращает память ОС в long-running процессах


Zend Memory Manager выделяет память большими блоками — чанки по ~2-4 МБ. Когда вы делаете unset($data) или переменная выходит из scope — память освобождается ВНУТРИ PHP, но операционная система об этом не узнаёт.

Чанки остаются закреплёнными за процессом до его завершения. Это сделано ради производительности: повторное использование уже выделенной памяти быстрее, чем постоянные запросы к ОС.

Почему это критично для RoadRunner, Laravel Queue, демонов?

В PHP-FPM каждый запрос = новый процесс, память автоматически очищается. В long-running процессах один worker обрабатывает тысячи задач — и каждый "пик" памяти становится новым baseline навсегда.

Ну а теперь про то, как себя обезопасить от всего этого?
- Использовать батчинг для выборок
- Ограничивать объём данных в памяти в рамках одной операции
- Обрабатывать данные потоково, без загрузки всего набора сразу
- Выносить тяжёлые операции в отдельные функции для автоматического освобождения локальных переменных
- Применять gc_mem_caches() для очистки внутренних пулов, и молиться, что поможет именно в вашем случае)
- Анализировать участки кода, создающие единовременные всплески в десятки МБ. Лучше не игнорировать всплески памяти, надеясь что “само освободится”

Что можно сделать на уровне PHP воркеров:
1. Можно использовать ротацию по количеству задач, например в RoadRunner pool.max_jobs=1000, Laravel Queue --max-jobs=1000. После выполнения 1000 задач, воркер будет перезапущен. Но не стоит полагаться на ротацию как на замену оптимизации, ведь пик может возникнуть в первых же запросах.
2. В RoadRunner — при необходимости включать soft-лимиты по памяти, при достижении которого воркер перезапускается.

Куда ещё может утекать память:
- Статические переменные, накапливающие данные
- Глобальные массивы, в которых растёт состояние
- Singleton-сервисы с неограниченными кешами
- ORM-кеши, которые не сбрасываются
- Списки подписчиков/слушателей, которые не удаляются
- Циклические ссылки объектов друг на друга.
  • 🔥 47
  • 🤯 10
  • 🤔 2
More from @php_fart
  1. Sep 9, 2026Попрошу перенести старт на конец сентября этого года. Лишь бы не жрать лимон 🍋
  2. Sep 9, 2026Если по моему промокоду придёт 50 фартанов, то я сожру лимон в прямом эфире перед докладом…
  3. Sep 9, 2026А у меня для вас вот такой промокод на Пыхник: PHPFART-26. Приходите смотреть, как я облаж…
  4. Sep 8, 2026Кстати, Роман и из JetBrains ушёл. Так что этот тред посвящён оплакиванию PHPStorm. Хороша…
  5. Sep 3, 2026Post #380
  6. Sep 2, 2026Роман Пронский окончательно уходит из PHPF. На его место заступает Брент (тоже JetBrains),…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →