首页 > 解决方案 > 遍历对象图时内存泄漏

问题描述

在教义中遍历对象图是否可以很好地扩展?

考虑这个类图实体(保证无环):

/**
  * @ORM\Entity()
  */
class Node {
    //...
    /**
     * @ORM\ManyToOne(targetEntity="Node")
     */
    public $child1;
    /**
     * @ORM\ManyToOne(targetEntity="Node")
     */
    public $child2;
    // ...
}

我像这样以迭代方式遍历对象图

$root = $this->em->find(Node::class, '1');
$stack = new Ds\Stack();
$stack->push($root->getId());
while(!$stack->isEmpty()) {
    $node = $em->find(Node::class, $stack->pop());
    work_on($node);
    if($node->child1) $stack->push($node->child1->getId());
    if($node->child2) $stack->push($node->child2->getId());
    $this->em->clear();
    gc_collect_cycles();
}

在实践中,我的堆栈大小高达 70.000,我正在遍历大小为 1.000.000 个节点的图。

所以这是深度优先搜索。我希望内存消耗保持不变,但它会不断上升,我看到 8 GB 甚至更多。我很确定不会保留对已处理节点的任何引用。

如果我将 UoW 清除并$em->getRepository(Node:class)->findAll()在开始时执行预取,我将获得 13 GB 内存,仅此而已。所以对我来说,清理似乎真的没有帮助,以某种方式释放处理过的对象确实失败了。

标签: phpmemorydoctrine-orm

解决方案


我找到了答案,部分在这里https://stackoverflow.com/a/25754165/5330805。并通过意识到这种情况:

在此处输入图像描述

假设我访问了 root,然后是 A,然后是 B(深度优先搜索)。

在 root 上工作时,我将 A、D、E、F 和 G 添加到堆栈中。
G 将被处理得很晚,但已经有对 A 的引用。所以当我完成 A 时,实际上仍然有对 A 的引用(在 G 中),所以它会留在内存中。

如果学说只是在 G 中加载了一个指向 A 的代理,这将不是问题,但是:

事实上,我的实体是一个继承层次结构(我有例如BigNode extends Nodeand SmallNode extends Node),并且我的关联被定义为基类Node

根据教义文档,在这种情况下,不可能只实例化一个未初始化的代理,所有的孩子都必须被获取。

该死。


推荐阅读