Механизм сборки мусора в PHP обрабатывает рекурсивные проблемы с ссылками? - PullRequest
1 голос
/ 28 июня 2011

В Perl это вызовет рекурсивную ссылку:

$a = \$a;

И счетчик ссылок $a никогда больше не достигнет 0 ...

Имеет ли PHP аналогичную проблему?

Если нет, то как PHP gc справляется с этим?

Ответы [ 3 ]

4 голосов
/ 28 июня 2011

Начиная с PHP 5.3.0, сборщик мусора PHP может и собирать графики объектов, содержащих циклы.

См. PHP: сбор циклов

3 голосов
/ 28 июня 2011

В PHP 5.3 появился новый сборщик мусора, который может нарушать такие циклические ссылки. В предыдущих версиях самоссылка вызывала утечку памяти и в конечном итоге убивала скрипт. 5.3 может сломать эталон и очистить должным образом.

http://www.php.net/manual/en/features.gc.collecting-cycles.php

2 голосов
/ 28 июня 2011

PHP это не Perl.Нет никакого способа создать фактические ссылки на память, поскольку это более известно из указателей Си. "Ссылки в PHP - это средство для доступа к одному и тому же содержимому переменной под разными именами. Они не похожи на указатели Си; например, вы не можете использовать арифметику указателей, используя их, они не являются реальными адресами памяти, [...]" ( Что такое ссылки ).

Такая рекурсивная ссылка на один и тот же адрес памяти фактически невозможно воспроизвести с помощью ссылок PHP и контейнеров zval,Поэтому PHP GC не должен иметь ничего общего с тем, что показано в отрывке perl.

(Если вы действительно можете создать такую ​​рекурсивную ссылку на память с PHP, которая, я сомневаюсь, возможна, пожалуйста, добавьте пример PHPкод на ваш вопрос.)

Рекурсивные ссылки в PHP

Как выглядит рекурсивная ссылка в PHP?На самом деле рекурсии нет.В PHP « имя переменной и содержимое переменной различаются » ( Что такое ссылки ), поэтому никогда не бывает истинной рекурсии в смысле ссылки на значение адреса памятиэтот адрес памяти.

Максимальное значение, которое вы можете получить, - это переменная, которая одновременно представляет собой псевдоним значения (стандартная переменная) и дополнительно является псевдонимом для того же значения::

a: (refcount=1, is_ref=1)='value'

Он имеет значение и ссылается на это значение.

«Сборка мусора» $a = &$a затем

Так что для этого примера PHP каксборка мусора вступает в игру?Ответ не на всех.На самом деле нет «циклической» ссылки для разрешения в этом $a = &$a случае.Это простой контейнер с количеством ссылок, равным единице, в то время как ссылка.Включение или выключение сборки мусора в PHP (или лучше сборка циклов или нет) не имеет никакого значения.Учитывая следующий тест-скрипт для случая $a = &$a:

function alocal($collectCycles) {
    $a = str_repeat('a', 1048576);

    $collectCycles && gc_collect_cycles();
    var_dump(xdebug_memory_usage());

    $a = &$a;

    $collectCycles && gc_collect_cycles();
    var_dump(xdebug_memory_usage());

    xdebug_debug_zval('a');
}

$collectCycles = false; // or true

$collectCycles ? gc_enable() : gc_disable();

$collectCycles && gc_collect_cycles();
var_dump(xdebug_memory_usage());

alocal($collectCycles);

$collectCycles && gc_collect_cycles();
var_dump(xdebug_memory_usage());

Запуск его с $collectCycles = false; или $collectCycles = true; не имеет ни малейшего значения:

Запуск без циклов сбора:

int(660680)
int(1709328)
int(1709328)
a: (refcount=1, is_ref=1)='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...'
int(660848)

Работа с циклами сбора:

int(660680)
int(1709328)
int(1709328)
a: (refcount=1, is_ref=1)='aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...'
int(660848)

Так что из соображений производительности и данного примера, похоже, что в PHP задействовано не так много сбора мусора, который имеет смысл, так какне является истинной циклической ссылкой.Это просто ценность и ссылка.Отмена установки псевдонима отменяет установку как значения, так и ссылки.Нет необходимости, чтобы GC вообще имел дело с циклическими ссылками.

Для чего полезна сборка мусора в PHP?

Ниже приведен пример демонстрационного кода, который провоцирует ситуацию, когда PHP будет иметь значенияв памяти, которые больше не доступны переменной (метке).Эти значения фактически бесполезны, потому что они существуют только на уровне интерпретатора и больше не могут быть доступны через PHP-код.Сборка мусора в PHP более или менее имеет дело с этими значениями.

Следующий скрипт создаст 10 экземпляров простого класса, который содержит только ссылку на себя (установленную в конструкторе класса) и содержит строкуразмером около одного мегабайта.

Для каждого экземпляра установлена ​​одна и та же переменная ($instance).Рядом с этой меткой объект сам ссылается на себя.Поэтому, правильно говоря, в первой переменной значение (конкретный экземпляр) помечено как $this->self (закрытый член), а вторая переменная - $instance.Поскольку $instance функционирует как метка для нового значения (следующего экземпляра), остается только метка внутри текущего экземпляра.Поэтому объект все еще существует.Однако он больше недоступен.

Использование сборки мусора теперь может освободить память, используемую для значений, которые больше не могут быть адресованы через метки.Это делается с помощью gc_collect_cycles().

Код:

define('CONSUME_PER_INSTANCE', 1048576); // in bytes

class StoreMore
{
    static $counter;
    private $self;
    private $store;
    private $number;
    public function __construct() {
        // assign object instance to a private member of itself (self-reference):
        $this->self = $this;
        $this->store = str_repeat('a', CONSUME_PER_INSTANCE); // consume some memory
        $this->number = ++ self::$counter;
    }
    public function __destruct() {
        echo 'Instance #', $this->number, ' destructed.', "\n";
    }
}

$baseMem = xdebug_memory_usage();

echo 'Memory use on start: ', number_format($baseMem, 0, '', ' '), "\n";

for($i=0;$i<10;$i++) {
    $instance = new StoreMore();
}

$diffMem = xdebug_memory_usage() - $baseMem;

echo 'Memory afterall: ', number_format(xdebug_memory_usage(), 0, '', ' '), ' - Diff: ', number_format($diffMem, 0, '', ' '), "\n";
echo 'Rough number of "instances" in afterall Diff: ', (int)($diffMem / CONSUME_PER_INSTANCE), "\n";


echo 'Garbage collecting starting...', "\n";
$result = gc_collect_cycles();
echo 'Garbage collecting ended: ', $result, "\n";

echo 'Memory after gc: ', number_format(xdebug_memory_usage(), 0, '', ' '), ' - Diff: ', number_format(xdebug_memory_usage() - $baseMem, 0, '', ' '), "\n";

unset($instance); // remove last instance

$lastDiffMem = xdebug_memory_usage() - $baseMem;

echo 'Memory after removal of last instance: ', number_format(xdebug_memory_usage(), 0, '', ' '), ' - Diff: ', number_format($lastDiffMem, 0, '', ' '), "\n";
echo 'Rough number of "instances" in last instance Diff: ', (int)($lastDiffMem / CONSUME_PER_INSTANCE), "\n";

echo 'Garbage collecting starting...', "\n";
$result = gc_collect_cycles();
echo 'Garbage collecting ended: ', $result, "\n";


echo 'Memory after final gc: ', number_format(xdebug_memory_usage(), 0, '', ' '), ' - Diff: ', number_format(xdebug_memory_usage() - $baseMem, 0, '', ' '), "\n";

Выход:

Memory use on start: 695 600
Memory afterall: 11 188 800 - Diff: 10 493 056
Rough number of "instances" in afterall Diff: 10
Garbage collecting starting...
Instance #1 destructed.
Instance #2 destructed.
Instance #3 destructed.
Instance #4 destructed.
Instance #5 destructed.
Instance #6 destructed.
Instance #7 destructed.
Instance #8 destructed.
Instance #9 destructed.
Garbage collecting ended: 27
Memory after gc: 1 745 496 - Diff: 1 049 896
Memory after removal of last instance: 1 745 552 - Diff: 1 049 800
Rough number of "instances" in last instance Diff: 1
Garbage collecting starting...
Instance #10 destructed.
Garbage collecting ended: 2
Memory after final gc: 696 328 - Diff: 728

Этот пример также показывает, что фактический экземпляр все еще «живет», поэтому код внутри объекта все еще может получать доступ к закрытым членам (как показывает деструктор).Экземпляры будут уничтожены, только если в игру вступит сборщик мусора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...