2011-08-18 101 views
6

在调试耗尽内存的代码时,我发现了一个非常有趣的问题,最重要的是我不知道如何解决这个问题。通过引用自身破坏对象

该应用程序大致由单个Survey对象组成,其中包含多个Question对象。 Question对象包含对他们所在调查的引用,例如,需要从其他问题获取答案。


下面的循环是造成内存溢出:

foreach ($survey_ids_arr as $survey_id) { 
    $Survey = new Survey($survey_id); 
} 

真的没什么异国情调的调查构造正在发生的事情;

  • 从数据库
  • 从数据库
  • 获取的所有问题的属性创建每一个问题一个问题对象获取其属性(传递一个参考$这个)
  • 将所有问题对象的内部阵列

并且从查看代码,您会说在每次迭代中,由于$ Survey变量被覆盖,所以对象从内存中清除。对??错误:)

随着脚本经过循环,内存堆积 - 调用显示调查对象使用的内存未按预期释放,此时另一个对象被分配给$Survey变量。即使在循环结束时调用unset($Survey)也不会释放内存。


罪魁祸首是传递给在创建问题的对象$this引用。这些引用防止对象从内存中清除 - 作为php.net状态手册:

析构函数方法将被调用,一旦到某个对象的所有引用都被删除

所以什么阻止对象被清理,是它对自身的引用。很好,嗯? :)

所以,问题是我的对象是一个记忆杀手。不幸的是,我想不出一个解决方案(除了写一个丑陋的方法来清除问题并从循环中调用它)。 Survey中的析构函数不是一个选项;如上所述,这不叫,因为 Question对象仍然有引用。

任何想法?有人必须已经遇到了这个问题 - 含父项的子对象并不是一个不常见的体系结构,是吗?

+0

问题是你的设计中存在一个矛盾:你想阻止对象从内存中清除,但你想清除它? – m0skit0

+1

在php 5.3中解决了这个问题。你使用什么PHP版本? – J0HN

+0

你是对的J0HN,我刚刚发现了错误报告! https://bugs.php.net/bug.php?id=33595 – Rijk

回答

2

所以,这里是答案:切换到PHP 5.3,因为这个问题已经在resolved里面了。如果您必须使用PHP < 5.3.0,则有责任释放在循环引用中捕获的对象。一种可能的办法是引入特殊方法,将剥去儿童物品的链接以允许它们被收集。GC

P.S.也可能对某人有用,Doctrine 1.2在模型中有这样的链接,因此它是内存泄漏的主题,特别是如果您从数据库中获取大量实体。如果您遇到了这个问题,请尝试(1)减少获取的实体数量(例如,以数组的形式进行水合),(2)处理具有原始sql请求的实体。