Symfony 信条:为什么可以';通过关联访问实体时是否释放内存?
我有一个Symfony 信条:为什么可以';通过关联访问实体时是否释放内存?,symfony,doctrine-orm,Symfony,Doctrine Orm,我有一个应用程序,它与应用程序文件有关系: /** * @ORM\OneToMany( * targetEntity="AppBundle\Entity\ApplicationFile", * mappedBy="application", * cascade={"remove"}, * orphanRemoval=true * ) */ private $files; 文件实体有一个存储二进制数据的字段,其大小可以达到2MB。当迭代大量应用程序及其文件时,PHP
应用程序
,它与应用程序文件
有关系:
/**
* @ORM\OneToMany(
* targetEntity="AppBundle\Entity\ApplicationFile",
* mappedBy="application",
* cascade={"remove"},
* orphanRemoval=true
* )
*/
private $files;
文件实体有一个存储二进制数据的字段,其大小可以达到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);
}
分离该对象应该告诉实体管理器停止关心该对象并取消引用它,但令人惊讶的是,它对内存使用量没有影响—它一直在增加
相反,我必须手动加载应用程序文件(而不是通过关联方法检索它们),并且内存使用不会增加。这项工作:
$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);
}
我使用xdebug\u debug\u zval
跟踪对$file
对象的引用。在第一个示例中,有一个额外的引用,这解释了内存膨胀的原因——PHP无法对其进行垃圾收集
有人知道这是为什么吗?这个额外的引用在哪里?如何删除它
EDIT:在循环结束时显式调用unset($file)
无效。此时仍然有两个对对象的引用(通过xdebug\u debug\u zval
验证)。一个包含在$file
中(我可以取消设置),但另一个地方我无法取消设置。在主循环结束时调用$this->em->clear()
,也没有效果
编辑2:SOLUTION:@origaminal的答案引导我找到了解决方案,因此我接受了他的答案,而不是提供我自己的答案
在第一种方法中,我通过$application
上的关联访问文件,这有一个副作用,即初始化我在外部循环中迭代的$application
对象上以前未初始化的$files
集合
调用$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();
}
$applications=$em->getRepository('AppBundle:Application')->findAll();
$count=count($applications);
对于($i=0;$i<$count;$i++){
foreach($applications[$i]->getFiles()作为$file){
$file->getData();
$em->detach($file);
未设置($文件);
}
$em->detach($applications[$i]);
未设置($i)应用程序;
//不需要强制GC,但这样做有助于测试。
gc_collect_cycles();
}
诀窍在于PHP的垃圾收集器,它的工作原理有点奇怪。首先,每次脚本需要内存时,它都会从RAM分配内存,即使您使用unset()
、$object=null
或其他技巧来释放内存,分配的内存也不会返回到操作系统,直到脚本未完成且与其相关的进程被终止
如何修复该问题
limit
、offset
参数运行所需脚本的命令,并以小批量多次重新运行所需脚本。这样,脚本将使用更少的内存,并且每次脚本完成时都会释放内存PDO
速度更快,成本更低对于内存中的对象可能导致泄漏的任务,您应该使用 为了返回
查询
对象而不是数组集合
,应该进行重构,然后从该查询对象中,可以轻松调用迭代()
方法,并在每次对象检查后清理内存
编辑
您有“隐藏引用”,因为detach
操作不会删除内存中的对象,它只告诉EntityManager
不再处理它。这就是为什么您应该使用my solution或unset()
带有php函数的对象。Cascade
EntityManager::detach
确实应该删除对enities的所有引用。但它不会自动对关联实体执行相同的操作
您需要通过添加关联的cascade
选项来级联此操作:
/**
* @ORM\OneToMany(
* targetEntity="AppBundle\Entity\ApplicationFile",
* mappedBy="application",
* cascade={"remove", "detach"},
* orphanRemoval=true
* )
*/
private $files;
现在$em->detach($app)
应该足以删除对应用程序
实体及其关联的应用程序文件
实体的引用
查找vs集合
我非常怀疑通过关联加载ApplicationFile
实体而不是使用存储库来findBy()
它们是问题的根源
确保通过关联加载时,集合将引用这些子实体。但是当父实体被取消引用时,整个树将被垃圾收集,除非有其他对这些子实体的引用
我怀疑您显示的代码是伪代码/示例代码,而不是生产中的实际代码。请仔细检查该代码以查找其他引用
清楚的
有时,清除整个EntityManager并合并几个实体是值得的。您可以尝试$em->clear()
或$em->clear('AppBundle\Entity\ApplicationFile')
清除无效
您是说清除EntityManager没有效果。这意味着您正在搜索的引用不在EntityManager(UnitOfWork)中,因为您刚刚清除了t
/**
* @ORM\OneToMany(
* targetEntity="AppBundle\Entity\ApplicationFile",
* mappedBy="application",
* cascade={"remove", "detach"},
* orphanRemoval=true
* )
*/
private $files;