1

我似乎记得一种设置__destruct类的方法,它可以确保一旦外部对象超出范围,循环引用就会被清除。但是,我构建的简单测试似乎表明这并没有像我预期/希望的那样表现。

有没有办法设置我的类,当最外面的对象超出范围时,PHP 会正确清理它们?

我不是在寻找编写此代码的替代方法,而是在寻找是否可以这样做,如果可以,如何?我通常会尽可能避免这些类型的循环引用。

class Bar {
    private $foo;
    public function __construct($foo) {
        $this->foo = $foo;
    }
    public function __destruct() {
        print "[destroying bar]\n";
        unset($this->foo);
    }
}

class Foo {
    private $bar;
    public function __construct() {
        $this->bar = new Bar($this);
    }
    public function __destruct() {
        print "[destroying foo]\n";
        unset($this->bar);
    }
}

function testGarbageCollection() {
    $foo = new Foo();
}

for ( $i = 0; $i < 25; $i++ ) {
    echo memory_get_usage() . "\n";
    testGarbageCollection();
}

输出如下所示:

60440
61504
62036
62564
63092
63620
 [ destroying foo ]
 [ destroying bar ]
 [ destroying foo ]
 [ destroying bar ]
 [ destroying foo ]
 [ destroying bar ]
 [ destroying foo ]
 [ destroying bar ]
 [ destroying foo ]
 [ destroying bar ]

我所希望的:

60440
 [ destorying foo ]
 [ destorying bar ]
60440
 [ destorying foo ]
 [ destorying bar ]
60440
 [ destorying foo ]
 [ destorying bar ]
60440
 [ destorying foo ]
 [ destorying bar ]
60440
 [ destorying foo ]
 [ destorying bar ]
60440
 [ destorying foo ]
 [ destorying bar ]

更新:

这个与 PHP > 5.3 相关的问题有几个很好的答案,但我选择了适用于 PHP < 5.3 的答案,因为它实际上与我的项目( PHP 5.2.x )有关。

4

4 回答 4

3

PHP >= 5.3 的解决方案可能是使用手册“垃圾收集”部分中解释的内容。

尤其是 gc_* 函数可能会引起人们的兴趣——请参阅gc_collect_cycles等。


对于您发布的代码部分,使用 PHP >= 5.3 :

  • 垃圾收集应该工作
  • 它只会在 PHP 认为有必要时运行

第二点非常重要:由于您的代码很短,因此不需要太多内存;这意味着垃圾收集不会在循环的每次迭代结束时运行:

  • 好的,这会释放一点内存
  • 但这不是必需的,因为无论如何还有很多内存

而且,由于垃圾收集需要时间,PHP 不会经常运行它。


前段时间我在我的博客上写了一篇关于这个的文章,在那里我做了一些测试;它是法语的,但是本节中的图表(这里没有语言障碍)清楚地表明垃圾收集器会在需要时偶尔运行一次。

于 2010-03-23T07:45:35.183 回答
2

http://docs.php.net/features.gc.collecting-cycles

当垃圾收集器打开时,只要根缓冲区满了,就会执行上述循环查找算法。根缓冲区的大小固定为 10,000 个可能的根(尽管您可以通过更改 PHP 源代码中 Zend/zend_gc.c 中的 GC_ROOT_BUFFER_MAX_ENTRIES 常量并重新编译 PHP 来改变它)。当垃圾收集器关闭时,循环查找算法将永远不会运行。但是,可能的根将始终记录在根缓冲区中,无论是否已使用此配置设置激活垃圾收集机制。

http://docs.php.net/features.gc.performance-considerations

首先,实现垃圾回收机制的全部原因是通过在满足先决条件后立即清理循环引用的变量来减少内存使用。在 PHP 的实现中,一旦根缓冲区已满,或者调用函数 gc_collect_cycles()时,就会发生这种情况。
于 2010-03-23T07:46:24.713 回答
2

由于__destruct仅在对象被回收时才调用,因此您不能为此使用它。您可以创建手动清理功能:

class Foo {
  private $bar;
  public function __construct() {
    $this->bar = new Bar($this);
  }
  public function cleanup() {
    $this->bar = null;
  }
  public function __destruct() {
    print "[destroying foo]\n";
  }
}

class Bar {
  private $foo;
  public function __construct($foo) {
    $this->foo = $foo;
  }
  public function __destruct() {
    print "[destroying bar]\n";
  }
}

function testGarbageCollection() {
  $foo = new Foo();
  $foo->cleanup();
}

我不确定这有多有用,但这确实是您唯一的选择< 5.3

于 2010-03-23T10:40:44.763 回答
0

从 5.3 开始,您可以

于 2010-03-23T07:45:26.193 回答