<?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.collecting-cycles.php',
    1 => 'Сбор циклических ссылок',
    2 => 'Сбор циклических ссылок',
  ),
  'up' => 
  array (
    0 => 'features.gc.php',
    1 => 'Сборка мусора',
  ),
  'prev' => 
  array (
    0 => 'features.gc.refcounting-basics.php',
    1 => 'Основы подсчёта ссылок',
  ),
  'next' => 
  array (
    0 => 'features.gc.performance-considerations.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.collecting-cycles" class="sect1">
   <h2 class="title">Сбор циклических ссылок</h2>
   <p class="para">
    Традиционно механизмы подсчёта ссылок в памяти вроде тех, которые работали в PHP раньше,
    не справлялись с утечками памяти, которые вызывали циклические ссылки;
    однако с PHP 5.3.0 разработчики языка реализовали синхронный алгоритм из исследования
    <a href="https://pages.cs.wisc.edu/~cymen/misc/interests/Bacon01Concurrent.pdf" class="link external">&raquo;&nbsp;Concurrent Cycle Collection in Reference Counted Systems</a>
    (англ. «Параллельный сбор циклических ссылок в системах подсчёта ссылок»), который решает эту проблему.
   </p>
   <p class="para">
    Полное описание работы алгоритма выходит за рамки раздела, но основы раздел объясняет.
    Вначале установим базовые правила. Первое, PHP продолжает хранить в памяти и не считает
    мусором контейнеры, значение бита refcount которых увеличилось.
    Zval-контейнер, количество ссылок в котором уменьшилось до нуля,
    освобождается из памяти. Поэтому циклические ссылки становятся мусорными, только когда
    переменных со ссылкой на контейнер не осталось, а значение аргумента refcount в контейнере не обнулилось.
    Второе, PHP умеет обнаруживать мусорные части в мусорных циклах путём
    уменьшения количества ссылок в контейнерах на единицу и проверки, в каких
    zval-контейнерах количество ссылок стало нулевым.
   </p>
   <p class="para">
     <div class="mediaobject">
      
      <div class="imageobject">
       <img src="images/12f37b1c6963c1c5c18f30495416a197-gc-algorithm.png" alt="Алгоритм сборки мусора" width="614" height="814" />
      </div>
     </div>
   </p>
   <p class="para">
    Алгоритм помещает возможные корни — zval-контейнеры — в «корневой буфер» и помечает
    корни «фиолетовыми», чтобы не вызывать проверку мусорных циклов при каждом уменьшении
    счётчика ссылок. Алгоритм следит и за тем, чтобы каждый возможный мусорный корень
    попадал в буфер только один раз. Механизм сборки мусора для каждого zval-контейнера внутри буфера
    стартует, только когда корневой буфер заполняется. Графически поведение показывает шаг A на рисунке выше.
   </p>
   <p class="para">
    На шаге B алгоритм выполняет поиск в глубину по каждому возможному корню,
    чтобы однократно уменьшить количество ссылок в каждом контейнере на единицу,
    и помечает корни «серыми». На шаге C алгоритм снова выполняет поиск в глубину
    от каждого корневого узла, чтобы ещё раз проверить количество ссылок для каждого zval-контейнера.
    Алгоритм помечает корни с нулевым количеством ссылок «белыми» (на рисунке — синим).
    А если количество ссылок в контейнере больше нуля, начиная с этого корня поиск идёт в глубину
    с обратным увеличением количества ссылок на единицу и пометкой корней «черными».
    На последнем шаге, D, алгоритм обходит корневой буфер
    и удаляет из него корни контейнеров. Алгоритм заодно проверяет, какие zval-контейнеры
    на предыдущем шаге он пометил «белыми». Каждый «белый» zval-контейнер освобождается из памяти.
   </p>
   <p class="para">
    Теперь, когда есть базовое представление о работе алгоритма, вернёмся к тому,
    как алгоритм интегрируется с PHP. По умолчанию сборщик мусора PHP включён.
    Параметр <a href="info.configuration.php#ini.zend.enable-gc" class="link">zend.enable_gc</a> в файле <var class="filename">php.ini</var>
    разрешает отключить сборку мусора.
   </p>
   <p class="para">
    При включённом сборщике мусора, алгоритм поиска циклических ссылок выполняется после каждого
    наполнения корневого буфера. Фиксированный размер корневого буфера равняется 10 000 возможных корней,
    хотя значение изменяется путём изменения значения константы <strong><code>GC_THRESHOLD_DEFAULT</code></strong>
    в файле <code class="literal">Zend/zend_gc.c</code> исходного кода PHP и пересборки PHP.
    При выключенном сборщике мусора алгоритм поиска циклических ссылок не запускается. Однако
    в корневой буфер всё равно записываются возможные корни, независимо от активации механизма
    сборки мусора через параметр конфигурации.
   </p>
   <p class="para">
    PHP прекратит запись возможных корней,
    если корневой буфер заполнится при выключенном механизме сборки мусора.
    Алгоритм не будет анализировать корни, которые не записал в буфер.
    Поэтому если корни окажутся мусором с циклическими ссылками, они вызовут утечку памяти,
    поскольку PHP не очистит их.
   </p>
   <p class="para">
    Причина постоянной записи корней в буфер даже при выключенном механизме сборки мусора
    состоит в том, что записать корни быстрее, чем каждый раз, когда удаётся найти корень,
    проверять, включили ли механизм сборки мусора. Однако сам механизм сборки мусора
    и алгоритм анализа иногда занимают много времени.
   </p>
   <p class="para">
    Кроме изменения параметра конфигурации <a href="info.configuration.php#ini.zend.enable-gc" class="link">zend.enable_gc</a>,
    доступен запуск механизма сборки мусора через вызов функции <span class="function"><a href="function.gc-enable.php" class="function">gc_enable()</a></span>
    и остановка механизма функцией <span class="function"><a href="function.gc-disable.php" class="function">gc_disable()</a></span>.
    Вызов этих функций даёт тот же эффект, что и включение или выключение механизма
    в настройках конфигурации.
    Возможна также принудительная сборка мусорных циклов, даже если корневой буфер ещё не заполнился.
    Для этого вызывают функцию <span class="function"><a href="function.gc-collect-cycles.php" class="function">gc_collect_cycles()</a></span>, которая
    возвращает количество циклических ссылок, которые собрал алгоритм.
   </p>
   <p class="para">
    Смысл включения и выключения механизма сборки мусора, а также ручного запуска механизма
    состоит в чувствительности отдельных частей приложения
    ко времени, когда автоматический запуск механизма сборки мусора не нужен.
    Отключение сборщика мусора в конкретных
    частях приложения создаёт риск утечки памяти, поскольку
    отдельные корни не поместятся в ограниченный
    корневой буфер. Поэтому лучше перед вызовом функции <span class="function"><a href="function.gc-disable.php" class="function">gc_disable()</a></span>
    вызвать функцию <span class="function"><a href="function.gc-collect-cycles.php" class="function">gc_collect_cycles()</a></span>,
    чтобы освободить память, риск потери которой возникает из-за возможных корней,
    которые алгоритм уже записал в корневой буфер.
    Это очистит буфер и даст больше места
    для хранения корней, пока механизм сбора мусорных циклов будет выключен.
   </p>
  </div><?php manual_footer($setup); ?>