Разбор работы refcount и Copy-on-Write в PHP
Рассмотрим неочевидное поведение подсчёта ссылок при работе с массивами в PHP 8.3.
В предыдущей статье был простой пример
$original = [1];
$copy = $original;
$copy[0] = 999;
xdebug_debug_zval('copy');
// Вывод: (refcount=1, is_ref=0)=array (0 => (refcount=0, is_ref=0)=999)
// Реальный refcount = 0 (новая копия)
Возник вопрос, почему после операции копирования refcount=0. Разберем подробнее в этой статье.
1. Пошаговый разбор примера
Шаг 1: Создание массива
$original = [1];
xdebug_debug_zval('original');
// Вывод: (refcount=2, is_ref=0)=array (0 => (refcount=0, is_ref=0)=1)
Объяснение:
- Реальный
refcount=1
(переменная$original
) - Xdebug добавляет +1 при выводе (поэтому показывает 2)
- Элемент массива
1
имеетrefcount=0
— это оптимизация для чисел
Шаг 2: Присваивание переменной
$copy = $original;
xdebug_debug_zval('original');
// Вывод: (refcount=3, is_ref=0)=array (...)
Объяснение:
- Реальный
refcount=2
($original
+$copy
) - Xdebug добавляет +1 (поэтому показывает 3)
- Обе переменные ссылаются на один zval
Шаг 3: Модификация массива (COW)
$copy[0] = 999;
xdebug_debug_zval('copy');
// Вывод: (refcount=1, is_ref=0)=array (0 => (refcount=0, is_ref=0)=999)
Ключевой момент:
- Срабатывает Copy-on-Write — создаётся новая копия массива
- Xdebug показывает
refcount=1
, значит реальныйrefcount=0
- Это означает, что только $copy ссылается на этот zval
- Если сделать
unset($copy)
— массив сразу удалится
2. Почему реальный refcount=0?
В PHP 8.3 действуют следующие правила:
- Новый zval после COW начинается с
refcount=0
- Переменная
$copy
— единственный владелец этого zval - Xdebug добавляет +1 при выводе (поэтому показывает 1)
- Элементы массива всегда
refcount=0
— это оптимизация
3. Визуализация в памяти
Операция | Состояние памяти |
---|---|
$original = [1] | [ZVAL1: refcount=1, value=[1]] |
$copy = $original | [ZVAL1: refcount=2, value=[1]] |
$copy[0] = 999 |
[ZVAL1: refcount=1, value=[1]] [ZVAL2: refcount=0, value=[999]] |
4. Практические выводы
- После COW новый массив имеет
refcount=0
(Xdebug показывает 1) - Это нормальное поведение оптимизированного PHP 8.3
- Элементы массива всегда
refcount=0
— не стоит беспокоиться - Для точных измерений используйте
memory_get_usage()
Статья актуальна для PHP 8.3. В более ранних версиях поведение refcount может отличаться.
Добавить комментарий