1
0
mirror of https://github.com/php/doc-ru.git synced 2026-03-24 07:42:22 +01:00
Files
archived-doc-ru/features/gc.xml
2025-09-12 01:09:51 +03:00

688 lines
39 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="utf-8"?>
<!-- EN-Revision: 23e84882d6654f995166d17e5610af6bf00ef18c Maintainer: irker Status: ready -->
<!-- Reviewed: no -->
<chapter xml:id="features.gc" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Сборка мусора</title>
<para>
Раздел описывает достоинства нового механизма сборки мусора
(Garbage Collection, или GC), который появился в PHP 5.3.
</para>
<sect1 xml:id="features.gc.refcounting-basics">
<title>Основы подсчёта ссылок</title>
<para>
Переменная PHP хранится в контейнере, который называется zval (англ. сокр.: Zend Value — Zend-значение).
Контейнер zval, кроме типа и значения переменной, также содержит два дополнительных
бита информации. Первый называется is_ref (англ. сокр.: Is Reference — ссылка ли?)
и представляет логическое значение, которое указывает, включил ли PHP переменную в «набор ссылок» или нет.
За счёт бита is_ref PHP-движок знает, как отличать обычные переменные от ссылок. Поскольку PHP разрешает
пользовательские ссылки, которые создают оператором &amp;, контейнер zval
также содержит внутренний механизм подсчёта ссылок для оптимизации работы памяти.
Вторая часть дополнительной информации называется refcount (англ. сокр.:
Reference Counter — счётчик ссылок) и содержит количество имён переменных, или другое название — символов,
которые указывают на этот zval-контейнер. Каждый символ хранится в таблице символов. У каждой
области видимости переменных своя таблица символов. PHP создаёт отдельную область видимости для главного скрипта,
который срабатывает при запросе из браузера, и отдельную область видимости для каждой функции или метода.
</para>
<para>
Контейнер zval создаётся при объявлении новой переменной, которой
присваивается константное значение, например:
<example>
<title>Создание нового контейнера zval</title>
<programlisting role="php">
<![CDATA[
<?php
$a = "new string";
?>
]]>
</programlisting>
</example>
</para>
<para>
В примере в текущей области видимости создаётся новый символ с именем "<literal>a</literal>"
и новый контейнер переменной с типом <type>string</type> и значением <literal>new string</literal>.
Бит is_ref по умолчанию задаётся равным &false;, поскольку не создали ни одной пользовательской
ссылки. Значение же бита refcount задаётся равным <literal>1</literal>, поскольку с этим контейнером
PHP связал только один символ. Обратите внимание, zval-контейнеры со значением &true; в бите is_ref
и значением <literal>1</literal> в бите refcount движок преобразовывает в контейнеры стандартных переменных
путём установки для бита is_ref значения &false;. Модуль <link xlink:href="&url.xdebug;">Xdebug</link>
умеет выводить эту информацию через функцию <function>xdebug_debug_zval</function>.
</para>
<para>
<example>
<title>Вывод информации о zval-контейнере</title>
<programlisting role="php">
<![CDATA[
<?php
$a = "new string";
xdebug_debug_zval('a');
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
a: (refcount=1, is_ref=0)='new string'
]]>
</screen>
</example>
</para>
<para>
Присваивание одной переменной другому имени переменной увеличивает счётчик ссылок.
</para>
<para>
<example>
<title>Увеличение счётчика ссылок контейнера zval</title>
<programlisting role="php">
<![CDATA[
<?php
$a = "new string";
$b = $a;
xdebug_debug_zval('a');
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
a: (refcount=2, is_ref=0)='new string'
]]>
</screen>
</example>
</para>
<para>
Значение счётчика ссылок здесь равно <literal>2</literal>, поскольку с одним
и тем же контейнером переменной PHP связал как символ
<varname>a</varname>, так и символ <varname>b</varname>. PHP достаточно умён, чтобы
не копировать сам контейнер, пока этого не требуется. Как только
счётчик ссылок refcount становится равным нулю, контейнеры переменных уничтожаются.
Счётчик ссылок refcount уменьшается на единицу, когда символ, который PHP связал с контейнером переменной,
выходит из области видимости (например, в конце функции),
или при удалении символа (например, при вызове языковой конструкции <function>unset</function>).
</para>
<para>
<example>
<title>Уменьшение счётчика ссылок контейнера zval</title>
<programlisting role="php">
<![CDATA[
<?php
$a = "new string";
$c = $b = $a;
xdebug_debug_zval('a');
$b = 42;
xdebug_debug_zval('a');
unset( $c );
xdebug_debug_zval('a');
?>
]]>
</programlisting>
&example.outputs;
<screen>
<![CDATA[
a: (refcount=3, is_ref=0)='new string'
a: (refcount=2, is_ref=0)='new string'
a: (refcount=1, is_ref=0)='new string'
]]>
</screen>
</example>
</para>
<para>
Вызов <literal>unset($a);</literal> в этом месте
удалит контейнер переменной из памяти, включая тип и значение.
</para>
<sect2 xml:id="features.gc.compound-types">
<title>Составные типы данных</title>
<para>
Хранение составных типов данных наподобие массивов (<type>array</type>)
и объектов (<type>object</type>) усложняется. Каждый элемент массива или каждое свойство объекта
хранится в отдельной таблице символов, в противоположность хранению
скалярных (<type>scalar</type>) значений одной и той же области видимости в одной таблице символов.
Следующий пример создаст сразу три zval-контейнера:
</para>
<para>
<example>
<title>Создание zval-контейнера для массива (<type>array</type>)</title>
<programlisting role="php">
<![CDATA[
<?php
$a = array('meaning' => 'life', 'number' => 42);
xdebug_debug_zval('a');
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
a: (refcount=1, is_ref=0)=array (
'meaning' => (refcount=1, is_ref=0)='life',
'number' => (refcount=1, is_ref=0)=42
)
]]>
</screen>
<para>Графически:</para>
<mediaobject>
<alt>Контейнеры для простого массива</alt>
<imageobject>
<imagedata fileref="en/features/figures/simple-array.png" format="PNG"/>
</imageobject>
</mediaobject>
</example>
</para>
<para>
PHP создаёт три zval-контейнера для символов: <varname>a</varname>, <varname>meaning</varname>
и <varname>number</varname>. Аналогичные правила применяются для увеличения и уменьшения
количества ссылок. В следующем примере в массив добавляется ещё один элемент,
которому устанавливается значение другого элемента массива:
</para>
<para>
<example>
<title>Добавление уже существующего элемента в массив</title>
<programlisting role="php">
<![CDATA[
<?php
$a = array('meaning' => 'life', 'number' => 42);
$a['life'] = $a['meaning'];
xdebug_debug_zval('a');
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
a: (refcount=1, is_ref=0)=array (
'meaning' => (refcount=2, is_ref=0)='life',
'number' => (refcount=1, is_ref=0)=42,
'life' => (refcount=2, is_ref=0)='life'
)
]]>
</screen>
<para>Графически:</para>
<mediaobject>
<alt>Контейнеры для простого массива со ссылками</alt>
<imageobject>
<imagedata fileref="en/features/figures/simple-array2.png" format="PNG"/>
</imageobject>
</mediaobject>
</example>
</para>
<para>
Из вывода модуля Xdebug видно, что как старый, так и новый элемент массива
теперь указывает на zval-контейнер, значение refcount которого равно <literal>2</literal>.
Хотя вывод модуля Xdebug показывает два zval-контейнера со значением <literal>'life'</literal>,
контейнеры одинаковы. Функция <function>xdebug_debug_zval</function> не показывает, что контейнеры одинаковы,
но добавление в вывод указателей памяти покажет.
</para>
<para>
Элемент удаляется из массива аналогично удалению символа
из области видимости: счётчик ссылок refcount того контейнера, на который указывает элемент массива, уменьшается.
Контейнер переменной удаляется из памяти, когда значение в бите refcount достигает нуля.
Пример:
</para>
<para>
<example>
<title>Удаление элемента из массива</title>
<programlisting role="php">
<![CDATA[
<?php
$a = array('meaning' => 'life', 'number' => 42);
$a['life'] = $a['meaning'];
unset($a['meaning'], $a['number']);
xdebug_debug_zval('a');
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
a: (refcount=1, is_ref=0)=array (
'life' => (refcount=1, is_ref=0)='life'
)
]]>
</screen>
</example>
</para>
<para>
Ситуация станет интереснее, если добавить массив сам в себя.
В следующем примере вводится оператор присваивания по ссылке, чтобы
PHP не создал копию массива.
</para>
<para>
<example>
<title>Пример добавления массива как элемента самого себя</title>
<programlisting role="php">
<![CDATA[
<?php
$a = array('one');
$a[] =& $a;
xdebug_debug_zval('a');
?>
]]>
</programlisting>
&example.outputs.similar;
<screen>
<![CDATA[
a: (refcount=2, is_ref=1)=array (
0 => (refcount=1, is_ref=0)='one',
1 => (refcount=2, is_ref=1)=...
)
]]>
</screen>
<para>Графически:</para>
<mediaobject>
<alt>Контейнеры массива с циклическими ссылками</alt>
<imageobject>
<imagedata fileref="en/features/figures/loop-array.png" format="PNG"/>
</imageobject>
</mediaobject>
</example>
</para>
<para>
Видно, что переменная массива <varname>a</varname>, и второй элемент
с индексом <varname>1</varname> теперь указывают на контейнер переменной, значение refcount которого равно
<literal>2</literal>. Символы «...» в выводе показывают рекурсию, что в этом примере
означает, что символы «...» указывают на исходный массив.
</para>
<para>
Как и раньше, при удалении переменной символ удаляется, а счётчик ссылок контейнера переменной,
на который указывает переменная, уменьшается на единицу.
Поэтому, если применить конструкцию unset к переменной <varname>$a</varname> после запуска приведённого кода,
счётчик ссылок контейнера переменной, на который указывают переменная <varname>$a</varname> и элемент массива
с индексом 1, уменьшится на единицу, с 2 до 1:
</para>
<para>
<example>
<title>Удаление переменной массива <varname>$a</varname></title>
<screen>
<![CDATA[
(refcount=1, is_ref=1)=array (
0 => (refcount=1, is_ref=0)='one',
1 => (refcount=1, is_ref=1)=...
)
]]>
</screen>
<para>Графически:</para>
<mediaobject>
<alt>Контейнеры после удаления массива с циклическими ссылками, которые демонстрируют утечку памяти</alt>
<imageobject>
<imagedata fileref="en/features/figures/leak-array.png" format="PNG"/>
</imageobject>
</mediaobject>
</example>
</para>
</sect2>
<sect2 xml:id="features.gc.cleanup-problems">
<title>Проблемы очистки</title>
<para>
Хотя ни в одной области видимости больше нет символа, который указывает
на структуру массива, структуру нельзя очистить, поскольку элемент массива с ключом 1 по-прежнему
указывает на этот же массив. Поскольку на структуру не указывает внешний символ,
пользователю недоступна очистка этой структуры; поэтому пользователь получает утечку памяти.
К счастью, PHP очистит эту структуру данных в конце запроса, но до того момента
данные будут занимать ценное место в памяти. Такая ситуация часто возникает
при реализации алгоритмов синтаксического анализа или других вещей, в которых дочерние элементы
указывают на родительские. С ещё большей вероятностью такая же ситуация возникает с объектами,
поскольку с объектами язык неявно работает «<link linkend="language.oop5.references">по ссылке</link>».
</para>
<para>
Не проблема, если такое случается раз или два,
но при тысяче или даже миллионе таких случаев утечки памяти уже станут проблемой.
Особенно в скриптах, которые работают долго, например, в демонах, в которых запрос
не заканчивается, или в крупных наборах модульных тестов.
Последний случай вызвал проблемы при запуске модульных тестов
для компонента Template библиотеки ez Components. В ряде случаев
требовалось больше 2 ГБ памяти, доступа к которой
на тестовом сервере не было.
</para>
</sect2>
</sect1>
<sect1 xml:id="features.gc.collecting-cycles">
<title>Сбор циклических ссылок</title>
<para>
Традиционно механизмы подсчёта ссылок в памяти вроде тех, которые работали в PHP раньше,
не справлялись с утечками памяти, которые вызывали циклические ссылки;
однако с PHP 5.3.0 разработчики языка реализовали синхронный алгоритм из исследования
<link xlink:href="&url.gc-paper;">Concurrent Cycle Collection in Reference Counted Systems</link>
(англ. «Параллельный сбор циклических ссылок в системах подсчёта ссылок»), который решает эту проблему.
</para>
<para>
Полное описание работы алгоритма выходит за рамки раздела, но основы раздел объясняет.
Вначале установим базовые правила. Первое, PHP продолжает хранить в памяти и не считает
мусором контейнеры, значение бита refcount которых увеличилось.
Zval-контейнер, количество ссылок в котором уменьшилось до нуля,
освобождается из памяти. Поэтому циклические ссылки становятся мусорными, только когда
переменных со ссылкой на контейнер не осталось, а значение аргумента refcount в контейнере не обнулилось.
Второе, PHP умеет обнаруживать мусорные части в мусорных циклах путём
уменьшения количества ссылок в контейнерах на единицу и проверки, в каких
zval-контейнерах количество ссылок стало нулевым.
</para>
<para>
<mediaobject>
<alt>Алгоритм сборки мусора</alt>
<imageobject>
<imagedata fileref="en/features/figures/gc-algorithm.png" format="PNG"/>
</imageobject>
</mediaobject>
</para>
<para>
Алгоритм помещает возможные корни — zval-контейнеры — в «корневой буфер» и помечает
корни «фиолетовыми», чтобы не вызывать проверку мусорных циклов при каждом уменьшении
счётчика ссылок. Алгоритм следит и за тем, чтобы каждый возможный мусорный корень
попадал в буфер только один раз. Механизм сборки мусора для каждого zval-контейнера внутри буфера
стартует, только когда корневой буфер заполняется. Графически поведение показывает шаг A на рисунке выше.
</para>
<para>
На шаге B алгоритм выполняет поиск в глубину по каждому возможному корню,
чтобы однократно уменьшить количество ссылок в каждом контейнере на единицу,
и помечает корни «серыми». На шаге C алгоритм снова выполняет поиск в глубину
от каждого корневого узла, чтобы ещё раз проверить количество ссылок для каждого zval-контейнера.
Алгоритм помечает корни с нулевым количеством ссылок «белыми» (на рисунке — синим).
А если количество ссылок в контейнере больше нуля, начиная с этого корня поиск идёт в глубину
с обратным увеличением количества ссылок на единицу и пометкой корней «черными».
На последнем шаге, D, алгоритм обходит корневой буфер
и удаляет из него корни контейнеров. Алгоритм заодно проверяет, какие zval-контейнеры
на предыдущем шаге он пометил «белыми». Каждый «белый» zval-контейнер освобождается из памяти.
</para>
<para>
Теперь, когда есть базовое представление о работе алгоритма, вернёмся к тому,
как алгоритм интегрируется с PHP. По умолчанию сборщик мусора PHP включён.
Параметр <link linkend="ini.zend.enable-gc">zend.enable_gc</link> в файле &php.ini;
разрешает отключить сборку мусора.
</para>
<para>
При включённом сборщике мусора, алгоритм поиска циклических ссылок выполняется после каждого
наполнения корневого буфера. Фиксированный размер корневого буфера равняется 10 000 возможных корней,
хотя значение изменяется путём изменения значения константы <constant>GC_THRESHOLD_DEFAULT</constant>
в файле <literal>Zend/zend_gc.c</literal> исходного кода PHP и пересборки PHP.
При выключенном сборщике мусора алгоритм поиска циклических ссылок не запускается. Однако
в корневой буфер всё равно записываются возможные корни, независимо от активации механизма
сборки мусора через параметр конфигурации.
</para>
<para>
PHP прекратит запись возможных корней,
если корневой буфер заполнится при выключенном механизме сборки мусора.
Алгоритм не будет анализировать корни, которые не записал в буфер.
Поэтому если корни окажутся мусором с циклическими ссылками, они вызовут утечку памяти,
поскольку PHP не очистит их.
</para>
<para>
Причина постоянной записи корней в буфер даже при выключенном механизме сборки мусора
состоит в том, что записать корни быстрее, чем каждый раз, когда удаётся найти корень,
проверять, включили ли механизм сборки мусора. Однако сам механизм сборки мусора
и алгоритм анализа иногда занимают много времени.
</para>
<para>
Кроме изменения параметра конфигурации <link linkend="ini.zend.enable-gc">zend.enable_gc</link>,
доступен запуск механизма сборки мусора через вызов функции <function>gc_enable</function>
и остановка механизма функцией <function>gc_disable</function>.
Вызов этих функций даёт тот же эффект, что и включение или выключение механизма
в настройках конфигурации.
Возможна также принудительная сборка мусорных циклов, даже если корневой буфер ещё не заполнился.
Для этого вызывают функцию <function>gc_collect_cycles</function>, которая
возвращает количество циклических ссылок, которые собрал алгоритм.
</para>
<para>
Смысл включения и выключения механизма сборки мусора, а также ручного запуска механизма
состоит в чувствительности отдельных частей приложения
ко времени, когда автоматический запуск механизма сборки мусора не нужен.
Отключение сборщика мусора в конкретных
частях приложения создаёт риск утечки памяти, поскольку
отдельные корни не поместятся в ограниченный
корневой буфер. Поэтому лучше перед вызовом функции <function>gc_disable</function>
вызвать функцию <function>gc_collect_cycles</function>,
чтобы освободить память, риск потери которой возникает из-за возможных корней,
которые алгоритм уже записал в корневой буфер.
Это очистит буфер и даст больше места
для хранения корней, пока механизм сбора мусорных циклов будет выключен.
</para>
</sect1>
<sect1 xml:id="features.gc.performance-considerations">
<title>Вопросы производительности</title>
<para>
Предыдущий раздел уже упоминал, что простой сбор корней
незначительно влияет на производительность, но это если сравнивать PHP 5.2 с PHP 5.3.
Хотя запись корней в буфер по сравнению с отсутствием такой записи
в PHP 5.2 замедляет работу приложения, другие изменения в работе
PHP 5.3 во время выполнения кода предотвратили даже проявление этой конкретной потери производительности.
</para>
<para>
Производительность страдает в двух главных областях. Первая область —
уменьшение размера памяти, которую PHP тратит на запись корней, а вторая —
задержка во время выполнения кода, когда механизм сборки мусора очищает память.
Рассмотрим обе проблемы.
</para>
<sect2 xml:id="features.gc.performance-considerations.reduced-mem">
<title>Уменьшение размера памяти</title>
<para>
Первая причина появления в языке механизма сборки
мусора, состоит в уменьшении размера памяти, которую занимает мусор,
путём очистки переменных с циклическими ссылками в тот момент, когда выполнятся
предварительные условия. В реализации PHP сборка мусора начинается, как
только заполняется корневой буфер или при вызове функции
<function>gc_collect_cycles</function>.
График ниже показывает, как скрипт под графиком занимает память
в PHP 5.2 и PHP 5.3, без учёта памяти, которую занимает сам PHP при запуске.
</para>
<para>
<example>
<title>Пример использования памяти</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
public $var = '3.14159265359';
public $self;
}
$baseMemory = memory_get_usage();
for ($i = 0; $i <= 100000; $i++) {
$a = new Foo();
$a->self = $a;
if ($i % 500 === 0) {
echo sprintf('%8d: ', $i), memory_get_usage() - $baseMemory, "\n";
}
}
?>
]]>
</programlisting>
<mediaobject>
<alt>Сравнение потребления памяти в PHP 5.2 и PHP 5.3</alt>
<imageobject>
<imagedata fileref="en/features/figures/gc-benchmark.png" format="PNG"/>
</imageobject>
</mediaobject>
</example>
</para>
<para>
В этом академическом примере создаётся объект, в котором устанавливается свойство,
которое указывает на сам объект. Когда в скрипте переменной <varname>$a</varname>
на следующей итерации цикла повторно присваивается значение,
происходит типичная утечка памяти.
В примере утекает память для двух zval-контейнеров — контейнера объекта
и контейнера свойства объекта, — но алгоритм находит только один корень: переменную, которую удалили.
Как только после 10 000 итераций (если PHP-сборка разрешает только
10 000 корней) корневой буфер заполняется, срабатывает механизм сборки мусора
и память, которую занимают эти корни, освобождается.
Этот процесс хорошо виден на неравномерном графике потребления памяти PHP 5.3:
после каждых 10 000 итераций график проседает.
Сам механизм в примере совершает не много работы, потому что
структура утечек проста. Из графика видно, что максимальное потребление памяти
в PHP 5.3 составило около 9 МБ, тогда как в PHP 5.2 потребление памяти продолжает расти.
</para>
</sect2>
<sect2 xml:id="features.gc.performance-considerations.slowdowns">
<title>Замедление работы</title>
<para>
Вторая область, в которой механизм сборки мусора влияет на производительность, —
потеря времени, которое требуется сборщику мусора для освобождения «утечки» памяти.
Чтобы понять степень влияния, изменим предыдущий скрипт путём
добавления количества итераций и удаления промежуточных показателей потребления памяти.
После изменения скрипт выглядит вот так:
</para>
<para>
<example>
<title>Влияние на производительность</title>
<programlisting role="php">
<![CDATA[
<?php
class Foo
{
public $var = '3.14159265359';
public $self;
}
for ($i = 0; $i <= 1000000; $i++) {
$a = new Foo();
$a->self = $a;
}
echo memory_get_peak_usage(), "\n";
?>
]]>
</programlisting>
</example>
</para>
<para>
Запустим скрипт два раза: с включённой опцией
<link linkend="ini.zend.enable-gc">zend.enable_gc</link> и без неё.
</para>
<para>
<example>
<title>Запуск скрипта</title>
<programlisting role="shell">
<![CDATA[
time php -dzend.enable_gc=0 -dmemory_limit=-1 -n example2.php
# и
time php -dzend.enable_gc=1 -dmemory_limit=-1 -n example2.php
]]>
</programlisting>
</example>
</para>
<para>
На тестовой машине первая команда выполняется примерно 10.7 секунды,
а вторая примерно 11.4 секунды. Это примерно на 7 % медленнее. Однако
максимальное потребление памяти скриптом уменьшилось на 98 % — с 931 до 10 МБ.
Этот тест производительности не научный и даже не представляет реальное приложение,
но показывает преимущества в работе с памятью, которые даёт механизм сборки мусора.
Хорошо то, что замедление скрипта каждый раз составляет одни и те же 7 %,
тогда как экономия памяти постоянно увеличивается по мере того, как алгоритм
во время выполнения скрипта обнаруживает всё больше циклических ссылок.
</para>
</sect2>
<sect2 xml:id="features.gc.performance-considerations.internal-stats">
<title>Внутренняя статистика сборщика мусора</title>
<para>
PHP умеет выдавать больше информации о том, как механизм сборки
мусора выполняется в PHP. Но для этого потребуется перекомпилировать PHP,
чтобы включить код теста производительности и сбора данных. До запуска команды
<literal>./configure</literal> с параметрами, которые требуются пользователю,
потребуется установить для переменной окружения <literal>CFLAGS</literal> значение
<literal>-DGC_BENCH=1</literal>. Следующая последовательность должна сработать:
</para>
<para>
<example>
<title>Пример перекомпиляции PHP для включения теста производительности сборки мусора</title>
<programlisting role="shell">
<![CDATA[
export CFLAGS=-DGC_BENCH=1
./config.nice
make clean
make
]]>
</programlisting>
</example>
</para>
<para>
При повторном запуске приведенного примера кода с двоичным файлом PHP, который
только что создали, после завершения выполнения PHP выведет следующее:
</para>
<para>
<example>
<title>Статистика сборки мусора</title>
<programlisting role="shell">
<![CDATA[
GC Statistics
-------------
Runs: 110
Collected: 2072204
Root buffer length: 0
Root buffer peak: 10000
Possible Remove from Marked
Root Buffered buffer grey
-------- -------- ----------- ------
ZVAL 7175487 1491291 1241690 3611871
ZOBJ 28506264 1527980 677581 1025731
]]>
</programlisting>
</example>
</para>
<para>
Самую информативную статистику показывает первый блок. Видно,
что механизм сборки мусора запускался 110 раз, и суммарно освободил
больше 2 миллионов записей в памяти. Как только механизм сборки мусора
сработал хотя бы один раз, показатель пика корневого буфера (Root buffer peak)
будет равняться 10 000.
</para>
</sect2>
<sect2 xml:id="features.gc.performance-considerations.conclusion">
<title>Заключение</title>
<para>
Сборщик мусора в PHP вызывает замедление работы только
во время работы алгоритма сборки циклических ссылок, тогда как
в стандартных скриптах меньшего размера производительность не падает.
</para>
<para>
Когда механизм сборки циклов все-таки запускается
для стандартных скриптов, объём памяти, которую экономит механизм,
разрешает одновременно запускать на сервере большее количество скриптов,
поскольку в целом скрипты занимают не так много памяти.
</para>
<para>
Преимущества заметнее для скриптов, которые работают долго —
большие наборы тестов или демоны. Новый механизм существенно сокращает утечки памяти
для приложений, которые работают с расширением <link xlink:href="&url.php.gtk;">PHP-GTK</link>,
которые часто выполняются дольше, чем скрипты для веба.
</para>
</sect2>
</sect1>
</chapter>
<!-- Keep this comment at the end of the file
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->