在调试一段耗尽内存的代码时,我发现了一个非常有趣的问题,最重要的是我不知道如何解决它。
该应用程序大致由一个 Survey
对象组成,该对象包含多个 Question
对象。 Question 对象包含对其所在调查的引用,例如,这是能够从其他问题中获取答案所必需的。
以下循环导致内存溢出:
foreach ( $survey_ids_arr as $survey_id ) {
$Survey = new Survey( $survey_id );
}
在 Survey 构造函数中并没有发生任何奇特的事情;
- 从数据库中获取其属性
- 从数据库中获取所有问题的属性
- 为每个问题创建一个问题对象(传递对 $this 的引用)
- 将所有问题对象添加到内部数组
通过查看代码,您会说在每次迭代中对象都从内存中清除,因为 $Survey 变量被覆盖。正确的??错了:)
当脚本通过循环时,内存正在堆积 - 添加 memory_get_usage()
调用显示 Survey 对象使用的内存没有按预期释放,此时另一个对象正在释放分配给 $Survey
变量。即使在循环结束时调用 unset( $Survey )
也不会释放内存。
罪魁祸首是在创建时传递给 Question 对象的对 $this
的引用。这些引用防止对象从内存中清除 - 正如 php.net 上的手册所述:
The destructor method will be called as soon as all references to a particular object are removed
那么阻止对象被清理的是它对自身的引用。不错吧? :)
所以,问题是我的对象是内存 killer 。不幸的是,我想不出一个解决方案(除了编写一个丑陋的方法来清除问题并从循环中调用它)。 Survey 中的析构函数不是一个选项;如上所述,这不被称为因为问题对象仍然有引用。
有什么想法吗?一定有人已经遇到过这个问题 - 包含父子对象的架构并不少见,对吗?
最佳答案
所以,这里是答案:切换到 PHP 5.3,因为这个问题已经 resolved在里面。如果您必须使用 < 5.3.0 的 PHP,您有责任释放在循环引用中捕获的对象。一种可能的方法是引入特殊方法,该方法将剥离指向子对象的链接,以允许它们被 GC
附言也可能对某些人有用,Doctrine 1.2
在模型中有这样的链接,因此它容易发生内存泄漏,尤其是当您从数据库中获取大量实体时。如果您遇到此问题,请尝试 (1) 减少获取的实体数量(例如,hydrate 作为数组),(2) 使用原始 sql 请求处理实体。
关于php - 使用对自身的引用销毁对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7107258/