Основний механізм керування памʼяттю в PHP — підрахунок посилань. Кожен zval знає, скільки змінних чи властивостей на нього вказують; щойно лічильник падає до нуля, памʼять звільняється негайно, без участі збирача. Саме тому у звичайному запиті про GC можна не думати.
Refcount не справляється з циклами. Якщо два обʼєкти тримають один одного, після unset зовнішніх змінних у кожного лишається одне посилання від сусіда, і нуль недосяжний. Для таких випадків існує збирач циклів: можливі корені накопичуються в root buffer, і коли він заповнюється, алгоритм обходить граф і звільняє недосяжні підграфи. Запуск не привʼязаний до часу, а з PHP 7.3 поріг адаптивний.
Проблемою GC стає в довгих процесах: воркери черг, Octane, RoadRunner, демони. Процес живе години, тому все, що не звільнилось, накопичується: цикли між обʼєктами, статичні кеші, реєстри слухачів, identity map ORM. Виглядає це як повільний ріст RSS воркера до OOM.
Лікування починається з діагностики, а не з gc_collect_cycles. Метрики memory_get_usage у логах воркера, ліміт памʼяті й кількості завдань для контрольованого перезапуску, WeakMap для кешів, привʼязаних до обʼєктів, і gc_status для розуміння, чи взагалі збирач знаходить сміття. Якщо не знаходить, витік у ваших статичних структурах, а не в циклах.
final class Node
{
public ?Node $peer = null;
}
$a = new Node;
$b = new Node;
$a->peer = $b;
$b->peer = $a; // цикл: refcount кожного = 2
unset($a, $b); // refcount кожного = 1, памʼять не звільнена
gc_collect_cycles(); // лише збирач циклів прибере обидва
// Типова пастка у довгому воркері: статичний кеш росте вічно
final class Registry
{
private static array $seen = [];
public static function remember(object $o): void { self::$seen[] = $o; }
}
// Безпечна альтернатива: WeakMap не тримає обʼєкт живим
$cache = new WeakMap;
$cache[$order] = computeTotals($order); // зникне разом з $order
// Контроль у воркері
if (memory_get_usage(true) > 256 * 1024 * 1024) {
exit(12); // супервізор перезапустить процес
}
Сильна відповідь містить практику: gc_collect_cycles у довгих воркерах, memory_get_usage у метриках і перезапуск воркера після N запитів. Ще краще розповісти про реальний витік, який ви знайшли.