【问题标题】:Does SplObjectStorage leave memory leak references if it destructs while objects are still attached?如果 SplObjectStorage 在仍然附加对象时销毁,它会留下内存泄漏引用吗?
【发布时间】:2013-01-08 22:02:15
【问题描述】:

如果一个 SplObjectStorage 实例在仍然附加一些对象的情况下解构,它是先隐式分离对象,还是由于 SplObjectStorage 的引用导致内存泄漏那些悬空的物体?我正在尝试确定是否有必要使用用户空间代码“分离在销毁之前留下的任何内容”以防止此类内存泄漏。

$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?

【问题讨论】:

    标签: php spl


    【解决方案1】:

    简单的答案是:它应该释放这两个对象。

    如果不是这样,这应该被认为是一个错误。

    测试:创建一个带有析构函数的类,并确保它被调用。

    【讨论】:

    • 我的实验似乎表明,在销毁之前显式分离它们会留下更少的 zval,而仅破坏 SplObjectStorage 而附加的项目仍然存在......但我缺乏阅读 zval 信息的经验让我怀疑自己。
    【解决方案2】:

    简而言之:没有。

    In Long:当$storage 被取消引用时,它的所谓“refCount”为零,这意味着不再引用此对象。现在,下次垃圾收集器运行时,它将清理该对象,并且从 $storage 引用的每个对象都将其 refCount 减一。现在发生了完全相同的情况:GC 注意到,没有任何东西引用对象并将释放它们(通常在同一个垃圾收集器周期内,因为为什么不呢?)

    【讨论】:

    • 我担心的是对两个 stdClass 对象的引用,而不是对存储对象的引用 ;-)
    • @ashnazg 是的,我知道,但您也必须查看存储对象。总结一下:只要一个对象的引用存在,gc就不会去碰它,但是如果不再有引用,gc就会清理它并确保不会发生内存泄漏。只要$storage 存在,就至少有一个对每个包含项的引用。如果您删除对$storage 的所有引用,则取决于是否存在其他引用是否将释放这些项目。
    • 与您同在@KingCrunch :-) 我担心的是,当 $storage 销毁时,它可能仍然“附加”了 $x 和 $y,因此 $x 和$y 还有一个参考。如果 $storage 的销毁没有隐式释放这些引用,那么它们会在 $storage 消失后徘徊。因此,一旦我稍后销毁 $x 和 $y,它们 就会作为内存泄漏而挥之不去,因为它们每个都有一个悬空引用。所以,我的问题具体是 $storage 是否在其自身的销毁周期内自行释放所有剩余的“附加”引用(如果有的话)。
    【解决方案3】:

    如果此测试的结构正确,则似乎表明在销毁存储之前分离物品与在物品仍连接时销毁存储相比没有明显差异。注释掉 detach() 块不会导致 check() 输出有任何可见的变化。

    <?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
    

    我认为当我在使用 SplObjectStorage 对象的类中编写显式用户区 detach() 步骤时,造成了我的自我困惑。我认为 PHP Bug #63917 [1] 似乎突出的迭代问题实际上是唯一的错误问题,这首先让我怀疑带有 destruct-with-attachments 场景的错误。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-17
      • 2022-12-01
      • 1970-01-01
      • 2014-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多