使用对自身的引用来破坏对象

Rij*_*ijk 6 php oop destructor

在调试一段耗尽内存的代码时,我发现了一个非常有趣的问题,最重要的是我不知道如何修复它.

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


以下循环导致内存溢出:

foreach ( $survey_ids_arr as $survey_id ) {
    $Survey = new Survey( $survey_id );
}
Run Code Online (Sandbox Code Playgroud)

Survey构造函数中没有发生异乎寻常的异常情况;

  • 从数据库中获取其属性
  • 从数据库中提取所有问题的属性
  • 为每个问题创建一个Question对象(将引用传递给$ this)
  • 将所有Question对象添加到内部数组

从查看代码开始,您会说在每次迭代中都会从内存中清除对象,因为$ Survey变量会被覆盖.对??错了:)

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


罪魁祸首是$this在创建时传递给Question对象的引用.这些引用阻止对象从内存中清除 - 正如php.net上的手册所述:

一旦删除了对特定对象的所有引用,就会调用析构函数方法

因此,阻止对象被清理的是它自身对它的引用.很好,对吧?:)

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

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

J0H*_*0HN 2

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

PS 也可能对某些人有用,Doctrine 1.2在模型中具有这样的链接,因此它容易发生内存泄漏,特别是当您从数据库中获取大量实体时。如果您遇到此问题,请尝试 (1) 减少获取的实体数量(例如,将水合物作为数组),(2) 使用原始 sql 请求处理实体。