<?php
include_once $_SERVER['DOCUMENT_ROOT'] . '/include/shared-manual.inc';
$TOC = array();
$TOC_DEPRECATED = array();
$PARENTS = array();
include_once dirname(__FILE__) ."/toc/features.gc.inc";
$setup = array (
  'home' => 
  array (
    0 => 'index.php',
    1 => 'PHP Manual',
  ),
  'head' => 
  array (
    0 => 'UTF-8',
    1 => 'ru',
  ),
  'this' => 
  array (
    0 => 'features.gc.refcounting-basics.php',
    1 => 'Основы подсчёта ссылок',
    2 => 'Основы подсчёта ссылок',
  ),
  'up' => 
  array (
    0 => 'features.gc.php',
    1 => 'Сборка мусора',
  ),
  'prev' => 
  array (
    0 => 'features.gc.php',
    1 => 'Сборка мусора',
  ),
  'next' => 
  array (
    0 => 'features.gc.collecting-cycles.php',
    1 => 'Сбор циклических ссылок',
  ),
  'alternatives' => 
  array (
  ),
  'source' => 
  array (
    'lang' => 'ru',
    'path' => 'features/gc.xml',
  ),
  'history' => 
  array (
  ),
);
$setup["toc"] = $TOC;
$setup["toc_deprecated"] = $TOC_DEPRECATED;
$setup["parents"] = $PARENTS;
manual_setup($setup);

contributors($setup);

?>
<div id="features.gc.refcounting-basics" class="sect1">
   <h2 class="title">Основы подсчёта ссылок</h2>
   <p class="para">
    Переменная PHP хранится в контейнере, который называется zval (англ. сокр.: Zend Value — Zend-значение).
    Контейнер zval, кроме типа и значения переменной, также содержит два дополнительных
    бита информации. Первый называется is_ref (англ. сокр.: Is Reference — ссылка ли?)
    и представляет логическое значение, которое указывает, включил ли PHP переменную в «набор ссылок» или нет.
    За счёт бита is_ref PHP-движок знает, как отличать обычные переменные от ссылок. Поскольку PHP разрешает
    пользовательские ссылки, которые создают оператором &amp;, контейнер zval
    также содержит внутренний механизм подсчёта ссылок для оптимизации работы памяти.
    Вторая часть дополнительной информации называется refcount (англ. сокр.:
    Reference Counter — счётчик ссылок) и содержит количество имён переменных, или другое название — символов,
    которые указывают на этот zval-контейнер. Каждый символ хранится в таблице символов. У каждой
    области видимости переменных своя таблица символов. PHP создаёт отдельную область видимости для главного скрипта,
    который срабатывает при запросе из браузера, и отдельную область видимости для каждой функции или метода.
   </p>
   <p class="para">
    Контейнер zval создаётся при объявлении новой переменной, которой
    присваивается константное значение, например:
    <div class="example" id="example-1">
     <p><strong>Пример #1 Создание нового контейнера zval</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$a </span><span style="color: #007700">= </span><span style="color: #DD0000">"new string"</span><span style="color: #007700">;<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

    </div>
   </p>
   <p class="para">
    В примере в текущей области видимости создаётся новый символ с именем &quot;<code class="literal">a</code>&quot;
    и новый контейнер переменной с типом <span class="type"><a href="language.types.string.php" class="type string">string</a></span> и значением <code class="literal">new string</code>.
    Бит is_ref по умолчанию задаётся равным <strong><code><a href="reserved.constants.php#constant.false">false</a></code></strong>, поскольку не создали ни одной пользовательской
    ссылки. Значение же бита refcount задаётся равным <code class="literal">1</code>, поскольку с этим контейнером
    PHP связал только один символ. Обратите внимание, zval-контейнеры со значением <strong><code><a href="reserved.constants.php#constant.true">true</a></code></strong> в бите is_ref
    и значением <code class="literal">1</code> в бите refcount движок преобразовывает в контейнеры стандартных переменных
    путём установки для бита is_ref значения <strong><code><a href="reserved.constants.php#constant.false">false</a></code></strong>. Модуль <a href="http://xdebug.org/" class="link external">&raquo;&nbsp;Xdebug</a>
    умеет выводить эту информацию через функцию <span class="function"><strong>xdebug_debug_zval()</strong></span>.
   </p>
   <p class="para">
    <div class="example" id="example-2">
     <p><strong>Пример #2 Вывод информации о zval-контейнере</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$a </span><span style="color: #007700">= </span><span style="color: #DD0000">"new string"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">xdebug_debug_zval</span><span style="color: #007700">(</span><span style="color: #DD0000">'a'</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

     <div class="example-contents screen">
<div class="cdata"><pre>
a: (refcount=1, is_ref=0)=&#039;new string&#039;
</pre></div>
     </div>
    </div>
   </p>
   <p class="para">
    Присваивание одной переменной другому имени переменной увеличивает счётчик ссылок.
   </p>
   <p class="para">
    <div class="example" id="example-3">
     <p><strong>Пример #3 Увеличение счётчика ссылок контейнера zval</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$a </span><span style="color: #007700">= </span><span style="color: #DD0000">"new string"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$b </span><span style="color: #007700">= </span><span style="color: #0000BB">$a</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">xdebug_debug_zval</span><span style="color: #007700">(</span><span style="color: #DD0000">'a'</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

     <div class="example-contents screen">
<div class="cdata"><pre>
a: (refcount=2, is_ref=0)=&#039;new string&#039;
</pre></div>
     </div>
    </div>
   </p>
   <p class="para">
    Значение счётчика ссылок здесь равно <code class="literal">2</code>, поскольку с одним
    и тем же контейнером переменной PHP связал как символ
    <var class="varname">a</var>, так и символ <var class="varname">b</var>. PHP достаточно умён, чтобы
    не копировать сам контейнер, пока этого не требуется. Как только
    счётчик ссылок refcount становится равным нулю, контейнеры переменных уничтожаются.
    Счётчик ссылок refcount уменьшается на единицу, когда символ, который PHP связал с контейнером переменной,
    выходит из области видимости (например, в конце функции),
    или при удалении символа (например, при вызове языковой конструкции <span class="function"><a href="function.unset.php" class="function">unset()</a></span>).
   </p>
   <p class="para">
    <div class="example" id="example-4">
     <p><strong>Пример #4 Уменьшение счётчика ссылок контейнера zval</strong></p>
     <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$a </span><span style="color: #007700">= </span><span style="color: #DD0000">"new string"</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">$c </span><span style="color: #007700">= </span><span style="color: #0000BB">$b </span><span style="color: #007700">= </span><span style="color: #0000BB">$a</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">xdebug_debug_zval</span><span style="color: #007700">(</span><span style="color: #DD0000">'a'</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">$b </span><span style="color: #007700">= </span><span style="color: #0000BB">42</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">xdebug_debug_zval</span><span style="color: #007700">(</span><span style="color: #DD0000">'a'</span><span style="color: #007700">);<br /><br />unset( </span><span style="color: #0000BB">$c </span><span style="color: #007700">);<br /></span><span style="color: #0000BB">xdebug_debug_zval</span><span style="color: #007700">(</span><span style="color: #DD0000">'a'</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
     </div>

     
<div class="example-contents"><p>
 Результат выполнения приведённого примера:
</p></div>

     <div class="example-contents screen">
<div class="cdata"><pre>
a: (refcount=3, is_ref=0)=&#039;new string&#039;
a: (refcount=2, is_ref=0)=&#039;new string&#039;
a: (refcount=1, is_ref=0)=&#039;new string&#039;
</pre></div>
     </div>
    </div>
   </p>
   <p class="para">
    Вызов <code class="literal">unset($a);</code> в этом месте
    удалит контейнер переменной из памяти, включая тип и значение.
   </p>

   <div class="sect2" id="features.gc.compound-types">
    <h3 class="title">Составные типы данных</h3>

    <p class="para">
     Хранение составных типов данных наподобие массивов (<span class="type"><a href="language.types.array.php" class="type array">array</a></span>)
     и объектов (<span class="type"><a href="language.types.object.php" class="type object">object</a></span>) усложняется. Каждый элемент массива или каждое свойство объекта
     хранится в отдельной таблице символов, в противоположность хранению
     скалярных (<span class="type">scalar</span>) значений одной и той же области видимости в одной таблице символов.
     Следующий пример создаст сразу три zval-контейнера:
    </p>
    <p class="para">
     <div class="example" id="example-5">
      <p><strong>Пример #5 Создание zval-контейнера для массива (<span class="type"><a href="language.types.array.php" class="type array">array</a></span>)</strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$a </span><span style="color: #007700">= array(</span><span style="color: #DD0000">'meaning' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'life'</span><span style="color: #007700">, </span><span style="color: #DD0000">'number' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">42</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">xdebug_debug_zval</span><span style="color: #007700">(</span><span style="color: #DD0000">'a'</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

      
<div class="example-contents"><p>
 Вывод приведённого примера будет похож на:
</p></div>

      <div class="example-contents screen">
<div class="cdata"><pre>
a: (refcount=1, is_ref=0)=array (
   &#039;meaning&#039; =&gt; (refcount=1, is_ref=0)=&#039;life&#039;,
   &#039;number&#039; =&gt; (refcount=1, is_ref=0)=42
)
</pre></div>
      </div>
      <div class="example-contents"><p>Графически:</p></div>
      <div class="mediaobject">
       
       <div class="imageobject">
        <img src="images/12f37b1c6963c1c5c18f30495416a197-simple-array.png" alt="Контейнеры для простого массива" width="593" height="143" />
       </div>
      </div>
     </div>
    </p>
    <p class="para">
     PHP создаёт три zval-контейнера для символов: <var class="varname">a</var>, <var class="varname">meaning</var>
     и <var class="varname">number</var>. Аналогичные правила применяются для увеличения и уменьшения
     количества ссылок. В следующем примере в массив добавляется ещё один элемент,
     которому устанавливается значение другого элемента массива:
    </p>
    <p class="para">
     <div class="example" id="example-6">
      <p><strong>Пример #6 Добавление уже существующего элемента в массив</strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$a </span><span style="color: #007700">= array(</span><span style="color: #DD0000">'meaning' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'life'</span><span style="color: #007700">, </span><span style="color: #DD0000">'number' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">42</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$a</span><span style="color: #007700">[</span><span style="color: #DD0000">'life'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$a</span><span style="color: #007700">[</span><span style="color: #DD0000">'meaning'</span><span style="color: #007700">];<br /></span><span style="color: #0000BB">xdebug_debug_zval</span><span style="color: #007700">(</span><span style="color: #DD0000">'a'</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

      
<div class="example-contents"><p>
 Вывод приведённого примера будет похож на:
</p></div>

      <div class="example-contents screen">
<div class="cdata"><pre>
a: (refcount=1, is_ref=0)=array (
   &#039;meaning&#039; =&gt; (refcount=2, is_ref=0)=&#039;life&#039;,
   &#039;number&#039; =&gt; (refcount=1, is_ref=0)=42,
   &#039;life&#039; =&gt; (refcount=2, is_ref=0)=&#039;life&#039;
)
</pre></div>
      </div>
      <div class="example-contents"><p>Графически:</p></div>
      <div class="mediaobject">
       
       <div class="imageobject">
        <img src="images/12f37b1c6963c1c5c18f30495416a197-simple-array2.png" alt="Контейнеры для простого массива со ссылками" width="593" height="143" />
       </div>
      </div>
     </div>
    </p>
    <p class="para">
     Из вывода модуля Xdebug видно, что как старый, так и новый элемент массива
     теперь указывает на zval-контейнер, значение refcount которого равно <code class="literal">2</code>.
     Хотя вывод модуля Xdebug показывает два zval-контейнера со значением <code class="literal">&#039;life&#039;</code>,
     контейнеры одинаковы. Функция <span class="function"><strong>xdebug_debug_zval()</strong></span> не показывает, что контейнеры одинаковы,
     но добавление в вывод указателей памяти покажет.
    </p>
    <p class="para">
     Элемент удаляется из массива аналогично удалению символа
     из области видимости: счётчик ссылок refcount того контейнера, на который указывает элемент массива, уменьшается.
     Контейнер переменной удаляется из памяти, когда значение в бите refcount достигает нуля.
     Пример:
    </p>
    <p class="para">
     <div class="example" id="example-7">
      <p><strong>Пример #7 Удаление элемента из массива</strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$a </span><span style="color: #007700">= array(</span><span style="color: #DD0000">'meaning' </span><span style="color: #007700">=&gt; </span><span style="color: #DD0000">'life'</span><span style="color: #007700">, </span><span style="color: #DD0000">'number' </span><span style="color: #007700">=&gt; </span><span style="color: #0000BB">42</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$a</span><span style="color: #007700">[</span><span style="color: #DD0000">'life'</span><span style="color: #007700">] = </span><span style="color: #0000BB">$a</span><span style="color: #007700">[</span><span style="color: #DD0000">'meaning'</span><span style="color: #007700">];<br />unset(</span><span style="color: #0000BB">$a</span><span style="color: #007700">[</span><span style="color: #DD0000">'meaning'</span><span style="color: #007700">], </span><span style="color: #0000BB">$a</span><span style="color: #007700">[</span><span style="color: #DD0000">'number'</span><span style="color: #007700">]);<br /></span><span style="color: #0000BB">xdebug_debug_zval</span><span style="color: #007700">(</span><span style="color: #DD0000">'a'</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

      
<div class="example-contents"><p>
 Вывод приведённого примера будет похож на:
</p></div>

      <div class="example-contents screen">
<div class="cdata"><pre>
a: (refcount=1, is_ref=0)=array (
   &#039;life&#039; =&gt; (refcount=1, is_ref=0)=&#039;life&#039;
)
</pre></div>
      </div>
     </div>
    </p>
    <p class="para">
     Ситуация станет интереснее, если добавить массив сам в себя.
     В следующем примере вводится оператор присваивания по ссылке, чтобы
     PHP не создал копию массива.
    </p>
    <p class="para">
     <div class="example" id="example-8">
      <p><strong>Пример #8 Пример добавления массива как элемента самого себя</strong></p>
      <div class="example-contents">
<div class="phpcode"><code><span style="color: #000000"><span style="color: #0000BB">&lt;?php<br /><br />$a </span><span style="color: #007700">= array(</span><span style="color: #DD0000">'one'</span><span style="color: #007700">);<br /></span><span style="color: #0000BB">$a</span><span style="color: #007700">[] =&amp; </span><span style="color: #0000BB">$a</span><span style="color: #007700">;<br /></span><span style="color: #0000BB">xdebug_debug_zval</span><span style="color: #007700">(</span><span style="color: #DD0000">'a'</span><span style="color: #007700">);<br /><br /></span><span style="color: #0000BB">?&gt;</span></span></code></div>
      </div>

      
<div class="example-contents"><p>
 Вывод приведённого примера будет похож на:
</p></div>

      <div class="example-contents screen">
<div class="cdata"><pre>
a: (refcount=2, is_ref=1)=array (
   0 =&gt; (refcount=1, is_ref=0)=&#039;one&#039;,
   1 =&gt; (refcount=2, is_ref=1)=...
)
</pre></div>
      </div>
      <div class="example-contents"><p>Графически:</p></div>
      <div class="mediaobject">
       
       <div class="imageobject">
        <img src="images/12f37b1c6963c1c5c18f30495416a197-loop-array.png" alt="Контейнеры массива с циклическими ссылками" width="533" height="144" />
       </div>
      </div>
     </div>
    </p>
    <p class="para">
     Видно, что переменная массива <var class="varname">a</var>, и второй элемент
     с индексом <var class="varname">1</var> теперь указывают на контейнер переменной, значение refcount которого равно
     <code class="literal">2</code>. Символы «...» в выводе показывают рекурсию, что в этом примере
     означает, что символы «...» указывают на исходный массив.
    </p>
    <p class="para">
     Как и раньше, при удалении переменной символ удаляется, а счётчик ссылок контейнера переменной,
     на который указывает переменная, уменьшается на единицу.
     Поэтому, если применить конструкцию unset к переменной <var class="varname">$a</var> после запуска приведённого кода,
     счётчик ссылок контейнера переменной, на который указывают переменная <var class="varname">$a</var> и элемент массива
     с индексом 1, уменьшится на единицу, с 2 до 1:
    </p>
    <p class="para">
     <div class="example" id="example-9">
      <p><strong>Пример #9 Удаление переменной массива <var class="varname">$a</var></strong></p>
      <div class="example-contents screen">
<div class="cdata"><pre>
(refcount=1, is_ref=1)=array (
   0 =&gt; (refcount=1, is_ref=0)=&#039;one&#039;,
   1 =&gt; (refcount=1, is_ref=1)=...
)
</pre></div>
      </div>
      <div class="example-contents"><p>Графически:</p></div>
      <div class="mediaobject">
       
       <div class="imageobject">
        <img src="images/12f37b1c6963c1c5c18f30495416a197-leak-array.png" alt="Контейнеры после удаления массива с циклическими ссылками, которые демонстрируют утечку памяти" width="463" height="144" />
       </div>
      </div>
     </div>
    </p>
   </div>

   <div class="sect2" id="features.gc.cleanup-problems">
    <h3 class="title">Проблемы очистки</h3>
    <p class="para">
     Хотя ни в одной области видимости больше нет символа, который указывает
     на структуру массива, структуру нельзя очистить, поскольку элемент массива с ключом 1 по-прежнему
     указывает на этот же массив. Поскольку на структуру не указывает внешний символ,
     пользователю недоступна очистка этой структуры; поэтому пользователь получает утечку памяти.
     К счастью, PHP очистит эту структуру данных в конце запроса, но до того момента
     данные будут занимать ценное место в памяти. Такая ситуация часто возникает
     при реализации алгоритмов синтаксического анализа или других вещей, в которых дочерние элементы
     указывают на родительские. С ещё большей вероятностью такая же ситуация возникает с объектами,
     поскольку с объектами язык неявно работает «<a href="language.oop5.references.php" class="link">по ссылке</a>».
    </p>
    <p class="para">
     Не проблема, если такое случается раз или два,
     но при тысяче или даже миллионе таких случаев утечки памяти уже станут проблемой.
     Особенно в скриптах, которые работают долго, например, в демонах, в которых запрос
     не заканчивается, или в крупных наборах модульных тестов.
     Последний случай вызвал проблемы при запуске модульных тестов
     для компонента Template библиотеки ez Components. В ряде случаев
     требовалось больше 2 ГБ памяти, доступа к которой
     на тестовом сервере не было.
    </p>
   </div>
  </div><?php manual_footer($setup); ?>