Garabage Collector в PHP
PHP использует автоматическую систему управления памятью. Она основана на подсчёте ссылок (Reference Counting) для немедленного освобождения памяти и сборщике циклов (Cycle Collector) для обработки циклических ссылок.
1. Основы подсчёта ссылок
В PHP каждая переменная хранится в структуре под названием zval.
- Простые типы (PHP 7+): такие типы как
null,bool,intиfloatхранятся непосредственно вzvalи не используют подсчёт ссылок. - Сложные типы:
string,array,object,resourceиreferenceиспользуют счётчик ссылок (refcount).
Как это работает:
- При создании переменной или её присваивании другой переменной
refcountбазовых данных увеличивается. - Когда переменная выходит из области видимости или удаляется через
unset(),refcountуменьшается. - Если
refcountдостигает нуля, PHP немедленно освобождает память.
$a = ["hello"]; // refcount = 1
$b = $a; // refcount = 2
unset($a); // refcount = 1
unset($b); // refcount = 0 -> Память освобождена
2. Сбор циклов (циклические ссылки)
Подсчёт ссылок сам по себе не может обработать циклические ссылки, когда два или более объекта ссылаются друг на друга.
$a = new stdClass();
$b = new stdClass();
$a->child = $b;
$b->parent = $a;
unset($a, $b);
В этом примере, даже после unset(), оба объекта по-прежнему имеют refcount равный 1, потому что ссылаются друг на друга. Без сборщика циклов это привело бы к утечке памяти.
Алгоритм сбора циклов (Bacon-Rajan)
PHP использует конкурентный алгоритм сбора циклов, который запускается, когда «корневой буфер» потенциальных циклов достигает определённого лимита (по умолчанию: 10 000).
- Корневой буфер: когда
refcountуменьшается, но остаётся > 0, PHP подозревает цикл и добавляетzvalв корневой буфер. - Маркировка (серый): сборщик мусора выполняет обход в глубину, начиная с корней, уменьшая
refcountкаждого найденного внутреннегоzval. - Сканирование (белый/чёрный):
- Если
refcountzvalтеперь равен 0, он помечается как мусор (белый). - Если
refcount> 0, он помечается как достижимый (чёрный), и егоrefcountвосстанавливается.
- Если
- Очистка: все «белые»
zvalудаляются из памяти.
Схема: сбор циклов GC
graph TD
A[Запуск GC] --> B[Пометить потенциальные корни как фиолетовые]
B --> C[Обход в глубину: уменьшить refcount — пометить с ерым]
C --> D{refcount == 0?}
D -- Да --> E[Пометить белым — мусор]
D -- Нет --> F[Восстановить refcount — пометить чёрным]
E --> G[Очистка: освобождение памяти]
F --> G
G --> H[Конец GC]
3. Влияние на производительность
CPU vs Память
- GC включён (по умолчанию): предотвращает утечки памяти, но создаёт периодическую нагрузку на CPU при запуске цикла сборки.
- GC выключен: немного более быстрое выполн ение (полезно для очень короткоживущих скриптов), но может привести к высокому потреблению памяти при наличии циклических ссылок.
Ручное управление
Управлять сборщиком мусора можно с помощью следующих функций:
gc_enable()/gc_disable(): включение и выключение сборщика.gc_collect_cycles(): ручной запуск цикла сборки.gc_status(): возвращает статистику текущего состояния GC.
Оптимизации в современном PHP
- PHP 7.3+: значительно улучшена производительность сборщика циклов.
- PHP 8.0+: улучшена обработка внутренних структур.
- PHP 8.3+: добавлена возможность настройки максимального количества корней через
zend.gc_max_rootsи расширен выводgc_status()(добавлены флагиrunning,protected,full).
Лучшие практики
- Для длительно работающих процессов (демоны, воркеры) всегда оставляйте GC включённым.
- Избегайте создания глубоких циклических структур, если производительность критична.
- Используйте
gc_mem_caches()(PHP 7+) для освобождения памяти, используемой менеджером памяти Zend Engine.