Почему после модификации массива refcount становится 0? Разбор работы Copy-on-Write в PHP 8.3

Разбор работы 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 может отличаться.

Комментарии

Добавить комментарий

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