php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #65340 Memory leak when using magic __set ?
Submitted: 2013-07-25 21:38 UTC Modified: 2017-02-16 12:38 UTC
Votes:1
Avg. Score:3.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:0 (0.0%)
Same OS:1 (100.0%)
From: vitalif at mail dot ru Assigned: nikic (profile)
Status: Closed Package: Class/Object related
PHP Version: 5.5.1 OS: Linux
Private report: No CVE-ID: None
 [2013-07-25 21:38 UTC] vitalif at mail dot ru
Description:
------------
Hello!

I've discovered that when setting properties via __set() the object takes much more memory than it should. It's reproducible at least on PHP 5.5 and 5.4. Is it a memory leak?

Test script:
---------------
<?php

// Memory leak somewhere around __set?

class A
{
    var $data = array();
    function __get($k)
    {
        return $this->data[$k];
    }
    function __set($k, $v)
    {
        return $this->data[$k] = $v;
    }
}

$b = new A();

for ($i = 0; $i < 500000; $i++)
    $b->{"a$i"} = 'abc';
var_dump(memory_get_usage()); // int(78318488) - why so big?
$c = clone $b;
unset($b);
var_dump(memory_get_usage()); // int(42220972) - OK

unset($c);
$b = new A();
for ($i = 0; $i < 500000; $i++)
    $b->__set("a$i", 'abc');
var_dump(memory_get_usage()); // int(42221492) - OK


Expected result:
----------------
I expect roughly the same memory usage at all three points. Like:

int(42220972)
int(42220972)
int(42221492)

Actual result:
--------------
The first value is much bigger:

int(78318488)
int(42220972)
int(42221492)

Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2013-07-26 01:51 UTC] felipe@php.net
See bug #48197
 [2013-07-27 21:32 UTC] vitalif at mail dot ru
I don't think bug #48197 is related to this issue... There are no memory leaks. I.e. extra alloc done by __call in that bug is freed without problem (if you remove "$b[$i] =" and just throw away the value after each call).

And here I describe that an identical object takes twice more memory if you write in its array property using __set.
 [2013-10-05 22:04 UTC] vitalif at mail dot ru
Anyone? I've rechecked it on 5.5.4 - the bug is still there.
 [2015-03-19 12:12 UTC] php at bof dot de
Just tested with 5.6.7, issue is reproducible.
 [2015-08-28 21:21 UTC] nikic@php.net
The reason for this are property recursion guards. In PHP 5 32bit these take 72 bytes per distinct property name that was used with __get/etc. so in this case it would be 72*500000 bytes, which matches your numbers.
 [2015-08-28 21:30 UTC] vitalif at mail dot ru
But of course it's still a bug? :)
 [2017-02-16 11:35 UTC] nikic@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: nikic
 [2017-02-16 11:35 UTC] nikic@php.net
This issue has been fixed in PHP 7.1. Memory usage should now be constant for typical uses (no recursion over different property names) of magic getters/setters.
 [2017-02-16 12:25 UTC] ealexs at gmail dot com
Can you please explain: "no recursion over different property names". 
You mean the magic setter should not trigger another magic setter ? Would this apply on the object itself ($this) or it can also affect other objects ?

Thanks, Alex
 [2017-02-16 12:38 UTC] nikic@php.net
@ealexs: A magic accessor should not trigger a magic accessor on a different property name on the same object. $a->__get('x') -> $a->__set('x') is fine. $a->__get('x') -> $b->__get('y') is fine. $a->__get('x') -> $a->__get('y') may still lead to unbounded memory growth, if this is done for many distinct property names.
 
PHP Copyright © 2001-2022 The PHP Group
All rights reserved.
Last updated: Tue Jan 18 13:03:14 2022 UTC