Bug #69639 [NEW]: Closure causes memory leak (Cyclic reference) when used as class property
| From: | tom at r dot je | Date: | Fri, 15 May 2015 10:15:26 +0000 |
| Subject: | Bug #69639 [NEW]: Closure causes memory leak (Cyclic reference) when used as class property | ||
| Groups: | php.bugs | ||
| Request: | Send a blank email to php-bugs+get-192672@lists.php.net to get a copy of this message | ||
From: tom at r dot je
Operating system: *
PHP version: 5.6.9
Package: Performance problem
Bug Type: Bug
Bug description:Closure causes memory leak (Cyclic reference) when used as class property
Description:
------------
Because closures inside classes implicitly have a reference to $this, if
a closure is used as a class property, this automatically creates a
cyclic reference.
Having read the page on GC here:
http://php.net/manual/en/features.gc.collecting-cycles.php
I can
understand the problem, but the issue this causes is a memory leak any
time a closure is used as a class property (Or in an array/data
structure in a class property)
Consider the following code:
<?php
class X {
private $largeStr;
private $closure;
public function foo() {
$this->largestr = str_repeat('ABC', 10000000);
$this->closure = function() {};
}
}
echo (memory_get_usage()/1024/1024) . 'mb<br >';
for ($i = 0; $i < 1000; $i++) {
$x = new X;
$x->foo();
unset($x);
echo (memory_get_peak_usage()/1024/1024) . 'mb<br >';
}
?>
Which outputs:
0.21446228027344mb
28.827682495117mb
57.438827514648mb
86.050003051758mb
114.66118621826mb
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to
allocate 30000001 bytes)
I have explicitly called unset() which I would hope would free the
memory used by the class, clearly it doesn't. Similarly, adding
public function __destruct() {
unset($this->closure);
}
to the class does not help.
I'm not sure there is an obvious fix for this but gc_collect_cycles() in
the loop does solve it, I have to wonder why unset() doesn't just
trigger gc_collect_cycles(). Presumably it can do it in a smarter way as
well because it doesn't have to look through every defined variable.
--
Edit bug report at https://bugs.php.net/bug.php?id=69639&edit=1
--
Try a snapshot (PHP 5.4): https://bugs.php.net/fix.php?id=69639&r=trysnapshot54
Try a snapshot (PHP 5.5): https://bugs.php.net/fix.php?id=69639&r=trysnapshot55
Try a snapshot (trunk): https://bugs.php.net/fix.php?id=69639&r=trysnapshottrunk
Fixed in SVN: https://bugs.php.net/fix.php?id=69639&r=fixed
Fixed in release: https://bugs.php.net/fix.php?id=69639&r=alreadyfixed
Need backtrace: https://bugs.php.net/fix.php?id=69639&r=needtrace
Need Reproduce Script: https://bugs.php.net/fix.php?id=69639&r=needscript
Try newer version: https://bugs.php.net/fix.php?id=69639&r=oldversion
Not developer issue: https://bugs.php.net/fix.php?id=69639&r=support
Expected behavior: https://bugs.php.net/fix.php?id=69639&r=notwrong
Not enough info: https://bugs.php.net/fix.php?id=69639&r=notenoughinfo
Submitted twice: https://bugs.php.net/fix.php?id=69639&r=submittedtwice
register_globals: https://bugs.php.net/fix.php?id=69639&r=globals
PHP 4 support discontinued: https://bugs.php.net/fix.php?id=69639&r=php4
Daylight Savings: https://bugs.php.net/fix.php?id=69639&r=dst
IIS Stability: https://bugs.php.net/fix.php?id=69639&r=isapi
Install GNU Sed: https://bugs.php.net/fix.php?id=69639&r=gnused
Floating point limitations: https://bugs.php.net/fix.php?id=69639&r=float
No Zend Extensions: https://bugs.php.net/fix.php?id=69639&r=nozend
MySQL Configuration Error: https://bugs.php.net/fix.php?id=69639&r=mysqlcfg