学说:为什么通过关联访问实体时我不能释放内存?

Bri*_*ian 8 symfony doctrine-orm

我有Application一个关系ApplicationFile:

/**
 * @ORM\OneToMany(
 *   targetEntity="AppBundle\Entity\ApplicationFile",
 *   mappedBy="application",
 *   cascade={"remove"},
 *   orphanRemoval=true
 * )
 */
private $files;
Run Code Online (Sandbox Code Playgroud)

文件实体具有存储二进制数据的字段,并且最大可以为2MB.迭代大量应用程序及其文件时,PHP内存使用量会增加.我想保持低调.

我试过这个:

$applications = $this->em->getRepository('AppBundle:Application')->findAll();
foreach ($applications as $app) {
  ...
  foreach ($app->getFiles() as $file) {
    ...
    $this->em->detach($file);
  }
  $this->em->detach($app);
}
Run Code Online (Sandbox Code Playgroud)

分离对象应告诉实体管理器停止关注此对象并取消引用它,但它令人惊讶地对内存使用量没有影响 - 它会不断增加.

相反,我必须手动加载应用程序文件(而不是通过关联方法检索它们),并且内存使用量不会增加.这有效:

$applications = $this->em->getRepository('AppBundle:Application')->findAll();
foreach ($applications as $app) {
  ...

  $appFiles = $this
      ->em
      ->getRepository('AppBundle:ApplicationFile')
      ->findBy(array('application' => $application));

  foreach ($appFiles as $file) {
    ...
    $this->em->detach($file);
  }
  $this->em->detach($app);
}
Run Code Online (Sandbox Code Playgroud)

我曾经xdebug_debug_zval跟踪$file对象的引用.在第一个例子中,有一个额外的引用,这解释了为什么内存膨胀 - PHP无法垃圾收集它!

有人知道为什么吗?这个额外的参考在哪里,我该如何删除它?

编辑:unset($file)在循环结束时显式调用无效.此时仍然有两个对象的引用(已证实xdebug_debug_zval).一个包含在$file(我可以解开),但还有另一个我不能解开的地方.$this->em->clear()在主循环结束时调用也没有效果.

编辑2:解决方案:@origaminal的回答让我得到了解决方案,所以我接受了他的答案,而不是提供我自己的答案.

在第一种方法中,我通过关联访问文件$application,这有一个副作用,就是$files$application我在外部循环中迭代的对象上初始化以前未初始化的集合.

调用$em->detach($application)并且$em->detach($file)只告诉Doctrine的UOW停止跟踪对象,但它不会影响$applications我正在迭代的数组,现在已经填充了$files其中占用内存的集合.

$application在完成它之后我必须取消设置每个对象以删除对加载的所有引用$files.为此,我修改了循环:

    $applications = $em->getRepository('AppBundle:Application')->findAll();
    $count = count($applications);
    for ($i = 0; $i < $count; $i++) {
        foreach ($applications[$i]->getFiles() as $file) {
            $file->getData();
            $em->detach($file);
            unset($file);
        }
        $em->detach($applications[$i]);
        unset($applications[$i]);

        // Don't NEED to force GC, but doing so helps for testing.
        gc_collect_cycles();
    }
Run Code Online (Sandbox Code Playgroud)

ori*_*nal 1

如果我们谈论的是您的第一个实现,您将在属性的 中拥有指向集合的额外链接PersistentCollection::coll-Application::files该对象是由 Doctrine 在Application实例化时创建的。

通过分离,您只需删除UoW对象的链接。

有不同的方法可以解决这个问题,但应该应用很多技巧。最好的方法可能是分离Application对象并取消设置它。

但最好还是使用更高级的方法进行批处理:其他答案中列出了一些方法。当前的方式强制原则使用代理并向数据库抛出额外的查询以获取当前对象的文件。

编辑

第一个和第二个实现之间的区别在于,在第二种情况下没有循环引用:Application::files保持未初始化PersistenceCollection( 中没有元素coll)。

要检查这一点 - 您可以尝试显式删除文件关联吗?