Re: references, circular references, oop, and garbage collection in PHP5

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hey Ray-

Thanks for the link!

Actually, the article didn't help directly, but it did spark an idea.

My main problem all along was trying to get a true reference to $this. For some reason reading the article led me to try "$this- >this", which works, to my surprise!

So, I now have a complete solution to my problem.... code follows...

ONE QUESTION REMAINS: is what I'm doing intended / publicly exposed behavior, or am I hacking and at risk of failure in future versions?

Thanks,
Alan

<?php

/**
* Playing around with proper reference counting of nested objects in PHP.
*
* The proper way to handle refCounting of nested objects is to have parents "retain" their kids, and have the kids use "weak references" to the parent.
*
*/

$mem0 = memory_get_usage();

for ($i=0;$i<10;$i++) {
    $a = new A;
    $b = new B;
    $a->addB($b);
$b = NULL; // kill the ref from a to b so as to GC b. If we've done things right, b will still exist and the next line will not fail.
    $a->printBs();
$a = NULL; // kill the local ref to a, which should be the only ref-counted ref to a if we've done things right; this should cause a (and thus b) to be gc'd NOW.
}

print "Leaked: " . (memory_get_usage() - $mem0) . "\n";
print "Done\n";
exit;

class A
{
    public $b = array();
    private $memWaste;

    function __construct()
    {
        $this->memWaste = "";
        for ($i = 0; $i < 1000; $i++) {
            $this->memWaste .= "1234567890";
        }
        print "new A{$this}\n";
    }
    function __destruct()
    {
        print "kill A{$this}\n";
    }

    // add a child B to our list. We want a ref-counted instance here.
    function addB($b)
    {
        $this->b[] = $b;    // refCounted desired in parent->child link
$b->setA($this->this); // so, this is apparently how you access a true reference to $this, because &$this doens't work.
    }

    function printBs()
    {
        foreach ($this->b as $b) {
            $b->sayHi();
        }
    }
}

class B
{
    public $a;
    private $memWaste;
    function __construct()
    {
        $this->memWaste = "";
        for ($i = 0; $i < 1000; $i++) {
            $this->memWaste .= "1234567890";
        }
        print "new B{$this}\n";
    }
    function __destruct()
    {
        print "kill B{$this}\n";
    }
    // refCount NOT desired in child->parent link, so use ref vars
    function setA(&$a)
    {
        $this->a = &$a;
    }

    function sayHi()
    {
        print "HI from {$this}\n";
    }
}
?>


On Dec 6, 2005, at 7:10 PM, Ray Hauge wrote:

I am uncertain on this, but I believe that the $this variable is already just a reference to the class you are calling it from. Then passing the reference by-reference to the addParent() method of the Child class could be what is causing your issue. I'd be curious to see what would happen if you took out the pass-by- reference and instead pass-by-value for the addParent() method. Then again, that doesn't particularly sound correct either.

This link might help. They cover a lot of advanced reference usage for PHP.

http://www.onlamp.com/pub/a/php/2002/09/12/php_foundations.html

Alan Pinstein wrote:

So.. I am having PHP5 memory management problems.

They are similar to those described in this thread:

http://aspn.activestate.com/ASPN/Mail/Message/php-Dev/1555640

(so maybe this question belongs on php-dev but I figured I'd try here first... seems like a userland question)

Basically I have an object model to represent db objects, and I am bulk-loading the objects via some PHP scripts. Sadly the scripts consume unbounded memory because of this problem.

I have done a lot of programming in C++ and Obj-C and the normal way to handle circular references is to have parents "retain" (keep ref- counted links) to their kids, and have the kids have "weak references" (non-ref-counted) links to their parents. This way, when the parent is no longer used, it will automatically 0-out the ref counts to all children it links too and things GC correctly.

Now, how to do this in PHP?

Well, there are no "documented" weak references. However, I figured out by trial that if you obtain a php-reference to an object, it doesn't bump the refcount.

Question #1: Is the fact that references to objects in the form $objRef = &$obj don't bump the refcount of $obj an intended behavior that can be counted on? If so, cool!

So, now that we have a way to do weak references, we should be able to implement a reasonable memory management scheme for parent-child objects.

Normally from the client side the interface should look something like:

$parent = new Parent();
$child = new Child();
$parent->addChild($child);

Where parent can have 0,n children and child can have 0,1 parent.

And all of parent's internal links to child should be refcounted, and the internal links from child to parent are weak (not ref- counted).

So based on the above discovery about references, I tried to implement this as such:

class Parent
{
    public $children = array();

    // add a child to our list. We want a ref-counted link here.
    function addChild($child)
    {
$this->children[] = $child; // refCounted desired in parent->child link
        $child->setParent($this);
    }
}

class Child
{
    public $parent;

    // set the parent object. We want a non-ref-counted link here.
    function setParent(&$parent)
    {
        // refCount NOT desired in child->parent link
        $this->parent = &$parent;
    }
}

Now, you'd expect this would work, but it doesn't. On a hunch, I changed the client code to:

$parent = new Parent();
$child = new Child();
$parent->addChild($child);
$child->setParent($child); // new line here - you can successfully create a reference to the object when not passed in as $this

Now, this works! However, it's not practical. The setParent call should work from within the parent object....

So what I figured out is that $this is a "pseudo variable" according to the docs, but I don't know what that means. Empirically I have figured out that it means you cannot create a reference to it.

Is this a feature or a bug? What's the workaround?

This is a serious problem for PHP scripts that need to do things that require large amounts of memory.

Please advise.

Thanks,
Alan


--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php


[Index of Archives]     [PHP Home]     [Apache Users]     [PHP on Windows]     [Kernel Newbies]     [PHP Install]     [PHP Classes]     [Pear]     [Postgresql]     [Postgresql PHP]     [PHP on Windows]     [PHP Database Programming]     [PHP SOAP]

  Powered by Linux