php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #80125 CallbackFilterIterator is leaking memory
Submitted: 2020-09-20 13:12 UTC Modified: 2020-09-23 20:08 UTC
Votes:1
Avg. Score:5.0 ± 0.0
Reproduced:1 of 1 (100.0%)
Same Version:1 (100.0%)
Same OS:1 (100.0%)
From: mvorisek at mvorisek dot cz Assigned:
Status: Duplicate Package: Scripting Engine problem
PHP Version: 7.4.10 OS: linux
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: mvorisek at mvorisek dot cz
New email:
PHP Version: OS:

 

 [2020-09-20 13:12 UTC] mvorisek at mvorisek dot cz
Description:
------------
see test script, explicit GC does not help

the issues seems present when the given callback to CallbackFilterIterator is bound (eg. not static)

https://3v4l.org/UpmHK

Test script:
---------------
class IteratorWithFilter
{
    public $generator;

    public function __construct(array $data)
    {
        $this->generator = new \ArrayIterator($data);
    }

    public function filter()
    {
        $this->generator = new \CallbackFilterIterator($this->generator, function ($row)  {
            return true;
        });
        
        return $this;
    }
}

function createIterator() {
    $iter = new IteratorWithFilter([['a'], ['b']]);
    return $iter->filter();
}

for ($i = 0; $i < 50 * 1000; $i++) {
    createIterator();
    
    if (($i % 1000) === 0) {
        // gc_collect_cycles(); // explicit GC does not help...
        echo round(memory_get_usage() / (1024 * 1024), 3) . " MiB\n";
    }
}

Expected result:
----------------
reported memory usage should stay below 1 MiB

Actual result:
--------------
0.375 MiB
1.719 MiB
3.071 MiB
4.36 MiB
5.774 MiB
7.064 MiB
8.353 MiB
9.892 MiB
...

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2020-09-23 16:19 UTC] kalle@php.net
-Status: Open +Status: Not a bug
 [2020-09-23 16:19 UTC] kalle@php.net
You should declare the closure as `static` and you will get the desired result:
https://3v4l.org/QPlkq
 [2020-09-23 16:27 UTC] mvorisek at mvorisek dot cz
No! The CallbackFilterIterator must be GCable like any other object.

I simplified the test script (to use bound function with simple condition), I can not use static and I also mentioned this in the description. This is a clear bug to me.
 [2020-09-23 16:45 UTC] kalle@php.net
Then create a closure, and pass it, clone + bindTo on assignment it to the scope which you want:

https://3v4l.org/gemWn
 [2020-09-23 20:04 UTC] mvorisek at mvorisek dot cz
this is not working as well, Closure::bindTo is defined like "duplicate and bind", so you must assign the result

https://3v4l.org/Ggt54

then the issue remains - CallbackFilterIterator is not GCed even if there is no reference to it, it is a clear bug to me and it should be fixed
 [2020-09-23 20:08 UTC] nikic@php.net
-Status: Not a bug +Status: Duplicate
 [2020-09-23 20:08 UTC] nikic@php.net
Duplicate of bug #65387.
 [2020-09-29 10:00 UTC] mvorisek at mvorisek dot cz
Here is a solution using WeakReference:

https://3v4l.org/U3h2Q

I wonder if this can be fixed in php directly - using proper GCable solution or even with WeakReference
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Nov 21 18:01:29 2024 UTC