php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #45954 memory leak with unset(array)
Submitted: 2008-08-30 16:12 UTC Modified: 2008-09-02 17:01 UTC
From: mail at milianw dot de Assigned:
Status: Not a bug Package: Performance problem
PHP Version: 5.2.6 OS: *
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 this is not your bug, you can add a comment by following this link.
If this is your bug, but you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: mail at milianw dot de
New email:
PHP Version: OS:

 

 [2008-08-30 16:12 UTC] mail at milianw dot de
Description:
------------
When you setup an array and unset() it afterwards, not all memory is freed. I've run the code below on various PHP versions, including 5.2.6 (via Xampp) on Linux and Windows and always get output like:

startup:        64296
array setup:    13789672
unsetted array: 129284

The end value is more than twice as large as the start value.

Also interestingly is that low values in the for loop [e.g. for($i = 0; $i < 1; ++$i)] result in outputs like

startup:       64296
array setup:    64864
unsetted array: 64864

Could it be that unset() relies on the Garbage Collector to do the work instead of really destroying the variables? But then I find it strange that even if I put a sleep(10) after the unset I still get the same outputs for "unsetted array".

Reproduce code:
---------------
<?php
echo "startup:        ".memory_get_usage()."\n";
$array = array();
for ($i = 0; $i < 100000; ++$i) {
    $array[] = 'foobar';
}
echo "array setup:    ".memory_get_usage()."\n";
unset($array);
echo "unsetted array: ".memory_get_usage()."\n";

Expected result:
----------------
My expectations would be that the value reported at the end would be nearly equal to the startup value.

Additionally a call to unset() should (imo) not rely on the GC but do the deleting itself instantaneously.

NOTE: I've seen http://bugs.php.net/bug.php?id=41713 which tells a similar story, but it should be fixed as far as the bug report tells. Additionally it was Windows only yet I spotted the described behaviour first on a Linux machine.


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2008-08-31 00:28 UTC] jani@php.net
unset() is not free(). PHP uses memory manager that does the actual garbage cleanup during request shutdown.
 [2008-09-01 17:48 UTC] mail at milianw dot de
So there is simply no way at all to delete / free / unset variables in PHP? But why does it work perfectly (i.e. like I imagine it should) when I unset a string, even a very large one? That one frees the memory instantaneously. But not so for arrays.

I simply cannot see the reasoning behind this. When a programmer calls unset he seems to know that this very variable can be freed, or not? So why do we need to wait for a GC to step in and do the actual cleanup? As far as my limited knowledge goes manual delete/free is compatible with a GC, or not? Or is it only possible to free simple strings, ints etc. but not arrays?

Take this code:

~~~~
<?php
function foo() {
    $string  = '';
    for ($i = 0; $i < 1000; ++$i) {
        $string .= 'asdfasdfasdf';
    }
}

$i = 0;
echo "setup:\t".memory_get_usage()."\n";

for (; $i < 10; ++$i) {
    foo();
    echo "run $i:\t".memory_get_usage()."\n";
}
~~~~

Output:
setup:  65524
run 0:  66108
run 1:  66184
run 2:  66248
and constant thereafter.

If I know change the code slightly:
~~~~
<?php
function foo() {
    $array  = '';
    for ($i = 0; $i < 1000; ++$i) {
        $array[] = 'asdfasdfasdf';
    }
}

$i = 0;
echo "setup:\t".memory_get_usage()."\n";

for (; $i < 10; ++$i) {
    foo();
    echo "run $i:\t".memory_get_usage()."\n";
}
~~~~

The output becomes:
setup:  65364                                            
run 0:  130356                                           
run 1:  130364                                           
run 2:  130384                                           
run 3:  130344
and thereafter either that value or 130364

Ok - it's not a leak per se, yet I wonder: A function I called which setup an array should free that memory (or should get its memory freed) after stepping out of it's scope, no? Just like it does with strings...

Also: I can append the following code to the last snippet to verify that the memory is _never_ freed:
~~~~
while (true) {
    echo memory_get_usage()."\n";
}
~~~~
I actually altered it a bit to break when the memory consumption changes, and could potentially wait for hours.

So is this really no bug? Could it not be that e.g. some internal hash pointers are left behind without getting deleted?
 [2008-09-02 08:44 UTC] scottmac@php.net
The internal memory manager is keeping tabs of the memory and will reuse 
it again if there is a memory allocation that it can fit in the block.

You can use valgrind and see that the memory is in fact free'd when the 
request ends.
 [2008-09-02 17:01 UTC] mail at milianw dot de
Yes, thank you. Thats an explanation which clarifies things for me.

Though one last thing: I was not speaking about a memory leak which
persists after the process is finished, but one while the process is
running. I was interested because while optimizing GeSHi I searched
for ways to reduce the memory consumption / memory peaks and spotted
this behaviour.

You can close this bug. Thanks again.
 
PHP Copyright © 2001-2020 The PHP Group
All rights reserved.
Last updated: Wed Apr 01 11:01:23 2020 UTC