-
-
Notifications
You must be signed in to change notification settings - Fork 505
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace wrapTransversable
generators to prevent memory leaks
#2709
Conversation
|
||
private bool $iteratorAdvanced = false; | ||
|
||
private bool $iteratorExhausted = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced by setting $iterator = null
, as it's already done for UnrewindableIterator
$this->storeCurrentItem(); | ||
$this->iterator = new IteratorIterator($iterator); | ||
$this->iterator->rewind(); | ||
if ($this->iterator->valid()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This checks for a dead cursor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work refactoring this. I've left a suggestion to refactor storeCurrentItems
to consolidate some logic, but LGTM aside from that.
Problem
A non-static generator created with a method of a class instance holds the reference to this instance. When assigned to a property of this instance, this creates a circular reference (instance -> generator -> instance). The reference is destroyed only when the generator is exhausted.
Most of the time, when the memory limit is sufficient, this is not an issue. The garbage collector should remove this circular references, but it runs automatically only when reaching the
GC_THRESHOLD_DEFAULT
.When working with a MongoDB Cursor, this is a single object which can fill up a lot of memory, that explains why
gc_collect_cycles()
is not run automatically.Solution
Using
IteratorIterator
instead ofGenerator
, we remove this circular reference . Also, in find the code simpler.Same as mongodb/mongo-php-library#694
Demonstration
In the following example, if
gc_collect_cycles()
is not called in the loop, we can see the memory leak. When callinggc_collect_cycles()
explicitly, we can see 300 "collected" references. The memory leak disappears when theGenerator
is replaced by anIteratorIterator
.Source code