While debugging a piece of code that was exhausting the memory, I found a very interesting problem, and most importantly I don't know how to fix it.
The application consists roughly of a single Survey
object, which contains a number of Question
objects. The Question objects contain a reference to the Survey they're in, this is needed to be able to fetch answers from other Questions for instance.
The following loop was causing the memory overflow:
foreach ( $survey_ids_arr as $survey_id ) {
$Survey = new Survey( $survey_id );
}
Nothing really exotic is happening in the Survey constructor;
and from looking at the code, you would say that in each iteration the object is cleared from memory because the $Survey variable is overwritten. Right?? Wrong :)
The memory is piling up as the script goes through the loop - adding memory_get_usage()
calls shows that the memory used by the Survey object isn't freed as expected, at the moment another object is assigned to the $Survey
variable. Even calling unset( $Survey )
at the end of the loop does not free the memory.
The culprit are the references to $this
that are passed to the Question objects upon creation. These references prevent the object to be cleared from memory - as the manual on php.net states:
The destructor method will be called as soon as all references to a particular object are removed
So what prevents the object from being cleaned up, is the references it has in it to itself. Nice, huh? :)
So, the problem is my object is a memory killer. Unfortunately, I can't think of a solution (other than writing an ugly method which clears the questions and calling that from the loop). The destructor in Survey is not an option; as stated above this is not called because the Question objects still have references.
Any ideas? Someone must have run into this problem already - the parent-containing-child-objects is not an uncommon architecture, is it?
So, here is the answer: switch to PHP 5.3, since this issue have been resolved in it. If you have to work with PHP < 5.3.0, it is your responsibility to free objects captured in circle references. One possible aproach is to introduce special method that will strip off links to child objects to allow them be collected by GC
P.S. also might be useful for somebody, Doctrine 1.2
have such a links in models, so it's subject for memory leaks, especially if you fetch lots of entities from your database. If you are facing this issues, try (1) reduce amount of entities fetched (e.g. hydrate as arrays), (2) process entities with raw sql requests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With