вторник, 17 декабря 2019 г.

Сборка мусора в php

Недавно встретился хороший пост для начинающих о сборке мусора (он же garbage collection) в php. Далее я попытаюсь перевести этот пост на русский и добавить немного собственных данных. Ссылка на оригинальный пост - в конце.

Начнем с того, что так как php - язык интерпретируемый, то вам не нужно заморачиваться управлением памятью - выделением памяти, и что более важно - очисткой памяти. Этим в php занимается специальный механизм, называемый сборкой мусора (или garbage collection, или же gc).

Сборка мусора работает тремя способами:
  1. При уходе переменной из области видимости
  2. При подсчете ссылок
  3. При сборе циклических ссылок
- Как только переменная уходит из области видимости и больше нигде не используется - она автоматически собирается gc. Также с помощью unset можно явно определить, что переменную пора собирать gc. Пример кода:

function display_var() {
    $foo = "bar";
    echo $foo;
}

$user = "Mister X";
unset($user);


В данном коде:
  • переменная $foo будет автоматически собрана gc сразу после завершения выполнения функции display_var
  • переменная $user будет собрана gc, так как она явно удалена с помощью unset

- С подсчетом ссылок разобраться чуть сложнее. Официальная часть здесь.

Кратко - для того чтобы понять, можно ли безопасно собрать переменную с помощью garbage collection, используется механизм подсчета ссылок. Данный механизм заключается в следующем - при создании переменной в php создается не просто переменная, а контейнер типа zval, в котором помимо собственно типа и значения переменной, хранится еще два поля - ref_count и is_ref. Далее, если вы присваиваете другой переменной значение этой переменной, то контейнер не копируется с имеющимися данными, а php просто увеличивает ref_count на единицу, так как контейнер используется уже двумя переменными. И так далее.

Как только в какой-то момент переменная удаляется (с помощью unset или ухода из зоны видимости), счетчик ref_count в контейнере уменьшается. Как только счетчик дошел до нуля - считается, что контейнер готов к сборке мусора.

Мониторить состояние контейнера можно при наличии расширения Xdebug с помощью функции xdebug_debug_zval.

- Сборка мусора при наличии циклических ссылок не так сложна, как предыдущий пункт. В этом случае сборка мусора активируется в тот момент, когда в памяти находится 10000 объектов с циклическими ссылками, и один из них уходит из области видимости. Значение 10000 установлено на уровне ядра php и может быть изменено только путем изменения исходного кода и его перекомпиляции. Однако, процесс сборки мусора можно запустить явно, не дожидаясь накопления 10000 объектов, с помощью метода gc_collect_cycles.

Также, так как сборка мусора при наличии циклических ссылок может потребовать значительное количество ресурсов, то такую сборку можно запретить одним из двумя способов:
  • вызвать метод gc_disable
  • установить значение zend.enable_gc в false в файле php.ini (gc_disable делает то же самое)

Также можно отметить, что изменения в php7.3 серьезно улучшили механизм сборки мусора - в оригинальном посте можно увидеть бенчмарки сравнения предыдущих релизов и версии 7.3, плюс появилась полезная функция gc_status, выводящая данные об использовании gc. А при наличии уже упомянутого Xdebug можно получить еще больше информации с помощью функции xdebug_start_gcstats.

Вот все основные моменты, что следует знать и помнить о сборке мусора. Оригинальная статья с некоторыми дополнительными плюшками - тут.

Комментариев нет:

Отправить комментарий