我正在编写一个 PHP cli 程序,用作队列系统的工作进程。
我原以为在这种情况下,PHP 会定期回收垃圾,不会一直达到内存限制。
但事实并非如此。
注意事项
- 运行在 PHP 7 上
- 这是一个长时间运行的脚本
zend.enable_gc = 1
- 没有全局变量
我正在编写一个 PHP cli 程序,用作队列系统的工作进程。
我原以为在这种情况下,PHP 会定期回收垃圾,不会一直达到内存限制。
但事实并非如此。
zend.enable_gc = 1
问题就在于这里。你必须手动调用gc_collect_cycles()
来触发垃圾回收。
我写了一堆代码来尝试追踪,最后只剩下两个脚本:
这个不会崩溃:
for($i = 0;$i < 100;$i++) {
useMemory();
gc_collect_cycles();
}
这个程序会崩溃:
for($i = 0;$i < 100;$i++) {
useMemory();
}
gc_collect_cycles
时,垃圾回收就不会发生,你会达到内存限制,PHP会自己终止。__destruct
方法的复杂性。(还有在错误跟踪器#60982上)。class Big {
private $data;
public function __construct($d = 0) {
for($i = 0;$i< 1024 * 10;$i++) {
$this->$i = chr(rand(97, 122));
}
}
}
function useMemory() {
$a = new Big();
$b = new Big();
$a->b = $b;
$b->a = $a;
}
gc_collect_cyles()
的原因是因为垃圾回收器在其循环收集算法中使用的根缓冲区最大值为10,000,而内存限制已经达到。每个Big
对象在我的系统上几乎占用 1 MB 的空间,每次迭代会添加 2 个根($a 和 $b)。在第 64 次迭代时,将存在 128 个根,并且将超过 128 MB 的内存限制。如果每个对象小了100倍,那么就可以“容纳” 10,000 个对象在 128 MB 的限制内,垃圾回收器会为您清除它们。但是,具有循环关系的大型对象可能需要手动调用gc_collect_cyles()
。 - molecularbear