unset не уничтожает класс полностью, если он передан куда либо по ссылке

mrsol

Новичок
unset не уничтожает класс полностью, если он передан куда либо по ссылке

Есть два класса, в одном создаётся другой, и другому передаётся по ссылке первый. Он у себя это запоминает. Потом пытюсь уничтожить первый класс командой unset . Класс пропадает, становится NULL и должен якобы пропасть класс который был создан внутри. Но вот функция __destruct отрабатывается, только тогда, когда скрипт заканчивает работу.
То есть остаётся где-то копия данного класса, к которой я не могу получить доступ.
Вопрос. Как нормально уничтожать такие объекты, чтобы отрабатывался __destruct. И все копии сколько их бы не было уничтожались.

Вот на этом коде проводил тестирование.
PHP:
<?php
  
  class A{
    public $parent_class;
    
    public function add_parent(&$class){
        echo "Add parent class\n";
        $this->parent_class = &$class;
    }
    
    public function __construct(){
        echo "Construct Class A\n";
    }
    
    public function __destruct(){
        echo "Desctruct class A\n";
    }
  }
  
  class B{
    public $one_class;
    
    public function test(){
        $this->one_class = new A;
        $this->one_class->add_parent(&$this);
    }
    
    public function __construct(){
        echo "Construct Class B\n";
    }
    
    public function __destruct(){
        echo "Desctruct class B\n";
    }
  }
  
  echo "init class B\n";
  $test_cl = new B();
  echo "run test func\n";
  $test_cl->test();
  echo "try unset B\n";
  unset($test_cl);
  echo "now this class='".var_export($test_cl, true)."' \n";
  echo "end script\n";
  
?>
Вывод дает следующий

init class B
Construct Class B
run test func
Construct Class A
Add parent class
try unset B
now this class='NULL'
end script
Desctruct class B
Desctruct class A


Получается что объект $test_cl после unset сталл равен NULL. Но при этом в памяти где-то висит копия и как я понимаю занимает место.
 

Fally

Новичок
PHP:
function deleteA() {
        $this->one_class->__destruct();
        $this->one_class = null;
}
Добавь его в класс B. Дело в том, что ты удаляешь объект В, но в нём ещё лежит единственная ссылка на объект А. Поэтому А не может сам удалиться, т.к. на него в скрипте ещё есть ссылки.
 

dark-demon

d(^-^)b
не поверишь, unset вообще ничего не уничтожает :) она просто присваивает переменной значение null.
исключение - ассоциативные массивы. там она удаляет и ключ и значение.
 

berkut

Новичок
тут ключевая строчка $this->one_class->add_parent(&$this);
если её закоментить, то всё удалится отлично. задница в том, что unset($test_cl); запросто удалил-бы своё св-во $this->one_class с объектом А внутри. Но в объекте А есть ссылка на сам объект Б. Но вообще стрёмно получается немного.
после unset($test_cl); мы никак не можем ролучить доступ ни к объекту А, ни к объекту Б, но оба они висят мёртвым грузом.
Может ктонибудь головастый прояснит, почему их принудительно пых не удаляет.
 

Fally

Новичок
спасибо) не знал о такой тонкости)) но в принуипе логично, счётчик ссылок на данные в памяти уменьшается на еденицу, и если была единственная ссылка, то и память очищается..

~~~~
berkut, там всё просто, А ссылается на В, и В ссылается на А.. циклические ссылки.. страшная вещь, их надо разрывать.
 

berkut

Новичок
unset вообще ничего не уничтожает
всё-же разница есть, = null vs. unset() в ссылках

-~{}~ 15.02.08 00:03:

а, ну понятно в принципе почему не удаляет с циклическими ссылками.

-~{}~ 15.02.08 00:13:

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

mrsol

Новичок
Автор оригинала: Fally
PHP:
function deleteA() {
        $this->one_class->__destruct();
        $this->one_class = null;
}
Добавь его в класс B. Дело в том, что ты удаляешь объект В, но в нём ещё лежит единственная ссылка на объект А. Поэтому А не может сам удалиться, т.к. на него в скрипте ещё есть ссылки.
Да так можно, но проблема в том что ссылок может быть множество и не обязательно что они будут в подкласах.
Тут главный вопрос. Как удалить это множество ссылок на данный объект, хотя бы узнать этот список


Вот на таком примере

PHP:
<?php
  
  
  class B{
    public $one_class;
    
    public function test(){
        global $global_c;
        $global_c->temp_class = &$this;
    }
    
    public function __construct(){
        echo "Construct Class B\n";
    }
    
    public function __destruct(){
        echo "Desctruct class B\n";
    }
  }
  
  class C{
    public $temp_class;
    
    public function __construct(){
        echo "Construct Class C\n";
    }
    
    public function __destruct(){
        echo "Desctruct class C\n";
    }
  }
  
  $global_c = new C();
  echo "init class B\n";
  $test_cl = new B();
  echo "run test func\n";
  $test_cl->test();
  echo "try unset B\n";
  unset($test_cl);
  echo "now this class='".var_export($test_cl, true)."' \n";
  echo "end script\n";
  
?>
$global_c не является подклассом класса $test_cl но в нём содержится ссылка на этот класс, и поэтому $test_cl не удаляется.
Как найти все ссылки?

а, ну понятно в принципе почему не удаляет с циклическими ссылками.
И в этом примере ссылки не циклические.



2dark-demon
не поверишь, unset вообще ничего не уничтожает она просто присваивает переменной значение null.
исключение - ассоциативные массивы. там она удаляет и ключ и значение.
Откуда такая уверенность?

Код
PHP:
<?php
class Foo {
    function __construct()
    {
        $this->bar = new Bar($this);
    }
}

class Bar {
    function __construct($foo = null)
    {
        $this->foo = $foo;
    }
}

while (true) {
    $foo = new Foo();
    unset($foo);
    echo number_format(memory_get_usage()) . "\\n";
}
?>
вызывает перегрузку памяти
33,552,336
33,552,696
PHP Fatal error: Allowed memory size of 33554432 bytes exhausted

А этот код не вызвает
PHP:
<?php
class Foo {
    function __construct()
    {
        $this->bar = new Bar($this);
    }
    function __destruct()
    {
        unset($this->bar);
    }
}

class Bar {
    function __construct($foo = null)
    {
        $this->foo = $foo;
    }
}

while (true) {
    $foo = new Foo();
    $foo->__destruct();
    unset($foo);
    echo number_format(memory_get_usage()) . "\\n";
}
?>
 

berkut

Новичок
ну последний пример, было-бы стрёмно, если-бы $global_c вот так внезапно, после удаления $test_cl ни на что не ссылалась.
 

Fally

Новичок
berkut
это не ошибка реализации сборщика мусора, таково внутреннее устройство PHP, посмотри доклад Деррика Ретанса с PHPConf2007, там как раз описан механизм внутренней работы PHP и как там всё устроено.
 

mrsol

Новичок
Ну технически я же убиваю сам объект а не ссылку на него. По крайней мере я так хочу. И если нет объекта то и ссылки на него нет.

Ну так всё таки, может кто-нить сказать как найти все ссылки на определённый объект.
 

berkut

Новичок
Fally а где я говорил о ошибке?? я говорил, что просто такая реализация, в целях экономии ресурсов
 

Fally

Новичок
mrsol, Переменные-объекты в PHP5 это всего лишь ссылки на эти самые объекты... в PHP4 было иначе..

-~{}~ 14.02.08 23:34:

berkut
прошу прощения, не так для себя интерпретировал слово "ограничения конкретной реализации"...
 

mrsol

Новичок
Автор оригинала: dark-demon
> Откуда такая уверенность?

божественное откровение

-~{}~ 14.02.08 23:41:

> Ну технически я же убиваю сам объект а не ссылку на него. По крайней мере я так хочу.

ты не должен этого хотеть :)
И где в "божественном откровении" именно указано это? Можно даже процетировать. А то я его раза 3 сегодня наверное просмотрел вдоль и поперёк и утверждение такого не видел.


Вот похоже нашёл эту проблему
http://bugs.php.net/bug.php?id=33595
И что плохо
Fixed with GC patch in CVS HEAD and PHP_5_3.


Так как же все таки узнать и убить все ссылки на объект????

Всем спасибо, кто откликнулся.

Пшол я спать. Утро вечера мудренее.
 

atv

Новичок
Так как же все таки узнать и убить все ссылки на объект
Стандартными средствами PHP никак.

Нужно, либо вызывать какой нибудь специальный метод объекта, который бы освобождал всё, на что сылается этот объект, либо организовать свой счётчик ссылок на объект.
 

berkut

Новичок
тони к сожалению молчит. может я пропустил чего в мане, но там вроде как не описано поведение именно при _циклических_ ссылках. посему не ясно, то-ли баг это, толи фича и чего ожидать в будущем.
mrsol
Ну технически я же убиваю сам объект а не ссылку на него.
unset($obj) - технически как-раз убивает ссылку на объект, хранящуюся в $obj - вполне логично.
 

mrsol

Новичок
Поставим вопрос ребром.
Как можно отследить из объекта, когда его передают по ссылке?
 
Сверху