Does SplObjectStorage leave memory leak references if it destructs while objects are still attached?

StackOverflow https://stackoverflow.com/questions/14224801

  •  14-01-2022
  •  | 
  •  

Question

If an SplObjectStorage instance destructs while still having some objects attached, does it implicitly detach the objects first, or does a memory leak result by the SplObjectStorage's references to those dangling objects? I'm trying to determine if userland code to "detach anything that's left before destructing" is necessary to prevent such a memory leak.

$storage = new SplObjectStorage();
$x = new stdClass();
$y = new stdClass();
$storage->attach($x);
$storage->attach($y);
$storage = null; 
// did not explicitly detach $x and $y... does $storage's destruction do it?
// or do the zval counts on $x and $y now off by one?
$x = null;
$y = null;
// at this point, are there two dangling references to $x and $y,
// simply because $storage did not dereference from them before destroying itself?
Was it helpful?

Solution

The simple answer is: it should free those two objects.

If this is not the case, this should be considered a bug.

To test: create a class with a destructor, and ensure that it's called.

OTHER TIPS

In short: No.

In Long: The moment $storage gets dereferenced it has a so called "refCount" of zero, which means, that there is no reference to this object anymore. Now the next time the garbage collectors runs it will clean up this object and every object referenced from $storage gets their refCount decreased by one. Now exactly the same happens: The GC notices, that there is nothing, that references the ojects and will free them (usually during the same garbage collector cycle, because why not?)

If this test is structured correctly, it seems to show that detaching items before destruction of storage has no visible difference when compared to destruction of storage while items are still attached. Commenting out the detach() block does not result in any visible changes in the check() output.

<?php
class MyStorage extends SplObjectStorage
{
    public function __destruct()
    {
        echo "__destruct() of ";
        var_dump($this);
        //parent::__destruct();  // there is no SplObjectStorage::__destruct()
    }
}
class Foo
{
    public function __destruct()
    {
        echo "__destruct() of ";
        var_dump($this);
    }
}

function check($message)
{
    global $storage, $x, $y, $z;

    echo $message, ':', PHP_EOL;

    echo '$storage:  ', xdebug_debug_zval('storage');
    echo '$x      :  ', xdebug_debug_zval('x');
    echo '$y      :  ', xdebug_debug_zval('y');
    echo '$z      :  ', xdebug_debug_zval('z');

    echo PHP_EOL, PHP_EOL;
}

check("Starting environment");

$storage = new MyStorage();
check("\$storage created");
$x = new Foo();
check("\$x created");
$y = new Foo();
check("\$y created");
$z = new Foo();
check("\$z created");

$storage->attach($x);
$storage->attach($y);
$storage->attach($z);
check("Everything is attached");

// comment out this detach() block for comparison testing
$storage->detach($x);
$storage->detach($y);
$storage->detach($z);
check("Everything is detached");

// the check() output here is key for comparing with the final check() output below

$storage = null;
check("$storage destructed");

$x = null;
check("$x destructed");

$y = null;
check("$y destructed");

$z = null;
check("$z destructed");

// final check() output appears here

I think my self confusion was created when I did write explicit userland detach() steps in my class where I use an SplObjectStorage object. I think the iteration issue that seems to be highlighted by PHP Bug #63917 [1] is actually the only buggy issue, which first made me suspect a bug with the destruct-with-attachments scenario.

[1] -- http://bugs.php.net/bug.php?id=63917

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top