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

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

 



/* comments inline */

On Dec 8, 2005, at 2:34 AM, Curt Zirzow wrote:

On Wed, Dec 07, 2005 at 09:30:36PM -0500, Alan Pinstein wrote:

On Dec 7, 2005, at 12:36 AM, Curt Zirzow wrote:

I'm not sure how you mean a weak reference, and well a refcount is
rather meaning less in php userland.

So, this gets interesting. I don't know if you're familiar with the
circular-reference problem. But if you have two instances that have
references to each other, even once you remove all references to the
objects, they will not be GC'd since they have a mutual deadlock on
each other:

$a = new MyObj;
$b = new MyObj;
$a->setB($b);		// does $this->b = $b;
$b->setA($a);		// does $this->a = $a;

$a = NULL;
$b = NULL;

The actual instances pointed to by $a and $b WILL NOT GET FREED HERE
as you would *wish*. However this is expected behavior.

I would have to disagree with the 'as you would *wish*' part.

if I pass something to a function/method via copy (vs reference) i
would hardly expect code outside the function/method to affect
their existance.

I'll ask you to reconsider :) Let me explain...

After both $a and $b are set to null, your script NO LONGER HAS ANY REFERENCES TO $a OR $b! The objects are now "orphaned" as far as you are concerned. So if you cannot access the objects, why would you still want them to take up memory?

This is why I say "as you would *wish*. The fact that the objects are not freed here is a memory leak. Now, the party responsible for the memory leak is the coder, NOT PHP. PHP is doing the "right" thing because according to its internal GC both $a and $b still have a refcount of 1.

So, it is up to the developer to make sure that they code in such a way that the objects do not have a refcount deadlock in such a situation. This is the point of my entire thread; I want to confirm the correct way to do this in PHP.

Apple's Cocoa documentation has a good explanation of this. Read the sections "Retaining Objects", "Retain Cycles", and "Weak References" that begin here. It's just a few paragraphs. Cocoa's memory management and garbage collection is very similar in concept to PHP's, except that you must EXPLICITLY control reference counts.

[ watch for wrapped link ]
http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/ Concepts/ObjectOwnership.html#//apple_ref/doc/uid/20000043-1000698

Only by changing MyObj to store "weak references", that is references
to the objects that are NOT reference-counted, can you get the GC to
free the instances.

function setB(&$B) { $this->b = &$a; }
function setA(&$B) { $this->a = &$b; }

Now, the instances will be freed when the $a and $b are null'd out.

I think you meant to type:

  function setB(&$a) { $this->b = &$a; }
  function setA(&$b) { $this->a = &$b; }

Oops yes.

And consider the definition written as:

  function setB(&$a) { $this->b = $a; }
  function setA(&$b) { $this->a = $b; }

does the $a = null; and $b = null; outside the class cause the
objects to get null'd inside the class as well?

Well, this is different from what I wrote; you took away the & operator on $a and $b in the assignment. This then causes the refcounts to be bumped, so in the code as YOU'VE re-written it, in fact nulling the objects outside the class DOES NOT cause them to be released.

However, let's talk about the version I wrote:

  function setB(&$a) { $this->b = &$a; }
  function setA(&$b) { $this->a = &$b; }

In this case, nulling out the $a and $b that were passed in DOES in fact release these linked objects. But that's OK, because you don't have a way to access these objects anymore anyway, as described above.

Now, this example is just for explanatory purposes; typically you do not have two objects that mutually store weak references to each other. Typical, one side of the two-way link typically is the "owner" and has a "refcounted" reference to the other object, while the other side of the link has the weak reference to the other object. Normally the parent object retains, and the child object has a weak reference.

So, while I now feel more confident of how references act with
respect to objects (which is, they act the same as they do for any
variable), I still am not sure what "$this->this" is and why it
worked so magically.

You lost me on your $this->this statement.

Well, in reality, with a parent-child situation, you have

$a->addChildObject($b);

class A
{
   function addChildObject($b)
   {
      $this->children[] = $b;	// refcount+1
      $b->setParent($this);
   }
}
class B
{
   function setParent(&$b)
   {
       $this->parent = &$b;
   }
}

However, I tried this, and it doesn't work. The $b->setParent($this) line still bumps the refcount. After experimenting, I realize that if I do $b->setParent($this->this) then it works as expected.

But what the heck is $this->this?

And this is my question, why does this work? Can I count on it? Why doesn't just referencing $this work as expected?

I understand that this is a bit complicated, as memory management often is. But memory leaks are a big deal when writing PHP CLI scripts that import say 75,000 records and leak 4k per iteration.

Alan

--
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