如果我有循环引用,我可以自动触发PHP垃圾收集吗?

时间:2010-03-23 07:34:36

标签: php garbage-collection circular-reference

我似乎想起了一种为类设置__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的这个问题有几个很好的答案&gt; 5.3,但我选择了适用于PHP的答案&lt; 5.3因为它实际上与我的项目有关(PHP 5.2.x)。

4 个答案:

答案 0 :(得分:3)

使用PHP&gt; = 5.3的解决方案可以使用本手册Garbage Collection部分中解释的内容。

特别是,gc_ *函数可能很有用 - 请参阅gc_collect_cycles等。


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

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

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

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

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


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

答案 1 :(得分: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() ,就会发生这种情况。

答案 2 :(得分: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();
}

我不确定这是多么有用,但这确实是你唯一的选择&lt; 5.3

答案 3 :(得分:0)

自5.3 you can

以来