我在查找脚本中的内存泄漏原因时遇到了问题。我有一个简单的存储库方法,增加了一个'计数'我的实体中的列数量为X:
public function incrementCount($id, $amount)
{
$query = $this
->createQueryBuilder('e')
->update('MyEntity', 'e')
->set('e.count', 'e.count + :amount')
->where('e.id = :id')
->setParameter('id', $id)
->setParameter('amount', $amount)
->getQuery();
$query->execute();
}
问题是,如果我在循环中调用它,则每次迭代都会占用内存使用量:
$doctrineManager = $this->getContainer()->get('doctrine')->getManager();
$myRepository = $doctrineManager->getRepository('MyEntity');
while (true) {
$myRepository->incrementCount("123", 5);
$doctrineManager->clear();
gc_collect_cycles();
}
我在这里缺少什么?根据Doctrine的advice on batch processing,我已经尝试了->clear()
。我甚至试过了gc_collect_cycles()
,但问题仍然存在。
我在PHP 5.5上运行了Doctrine 2.4.6。
答案 0 :(得分:13)
我通过在命令中添加--no-debug
来解决这个问题。事实证明,在调试模式下,探查器将有关每个查询的信息存储在内存中。
答案 1 :(得分:9)
我刚遇到同样的问题,这些是为我解决的问题:
正如他们在答案中提到的那样,设置--no-debug
(例如:php app/console <my_command> --no-debug
)对于Symfony控制台命令中的性能/内存至关重要。使用Doctrine时尤其如此,因为没有Doctrine,Doctrine将进入调试模式,这会消耗大量的额外内存(每次迭代都会增加)。有关详细信息,请参阅Symfony文档here和here。
您还应始终指定环境。默认情况下,Symfony使用dev
环境进行控制台命令。 dev
环境通常不针对内存,速度,CPU等进行优化。如果要迭代数千个项目,则应该使用prod
环境(例如:{{1 }})。有关详细信息,请参阅here和here。
提示:我创建了一个名为php app/console <my_command> --env prod
的环境,我专门为运行控制台命令而配置了这个环境。以下是有关how to create additional Symfony environments。
如果运行大更新,您应该选择可以使用的内存量。如果您认为可能存在泄漏,这一点尤为重要。您可以使用console
(例如php -d memory_limit=x
)为Command指定内存。注意:您可以将限制设置为php -d memory_limit=256M
(通常是php cli的默认值),让命令在没有内存限制的情况下运行,但这显然很危险。
使用上述提示运行大更新的格式良好的控制台命令如下所示:
-1
在循环中使用Doctrine的ORM时,另一个巨大的问题是使用Doctrine的IterableResult(参见Doctrine Batch Processing docs)。这在所提供的示例中没有帮助,但通常在进行此类处理时,它会超出查询结果。
如果您正在做的部分工作是对数据进行更改,则应定期刷新而不是每次迭代。冲洗是昂贵且缓慢的。你冲洗的次数越少,命令完成的速度就越快。但请记住,Doctrine会将未刷新的数据保存在内存中。因此,刷新的频率越低,您需要的内存就越多。
您可以使用以下内容来刷新每100次迭代:
php -d memory_limit=256M app/console <acme>:<your_command> --env=prod --no-debug
还要确保在循环结束时再次刷新(用于刷新最后的&lt; 100条目)。
跟踪命令在运行时消耗的内存量非常有用。您可以通过输出PHP内置的memory_get_usage()函数返回的响应来实现。
祝你好运!答案 2 :(得分:4)
您每次迭代都会浪费内存。更好的方法是准备查询一次并多次交换参数 。例如:
class MyEntity extends EntityRepository{
private $updateQuery = NULL;
public function incrementCount($id, $ammount)
{
if ( $this->updateQuery == NULL ){
$this->updateQuery = $this->createQueryBuilder('e')
->update('MyEntity', 'e')
->set('e.count', 'e.count + :amount')
->where('e.id = :id')
->getQuery();
}
$this->updateQuery->setParameter('id', $id)
->setParameter('amount', $amount);
->execute();
}
}
正如你所提到的,你可以在这里使用批量处理,但首先尝试一下,看看有多好(如果有的话)......
答案 3 :(得分:4)
Doctrine会记录您所做的任何查询。如果你进行大量的查询(通常在循环中发生),Doctrine会导致巨大的内存泄漏。
您需要禁用Doctrine SQL Logger来克服这个问题。
我建议仅对循环部分执行此操作。
在循环之前,获取当前记录器:
$sqlLogger = $em->getConnection()->getConfiguration()->getSQLLogger();
然后禁用SQL Logger:
$ EM-&GT;的getConnection() - &GT; getConfiguration() - &GT; setSQLLogger(空);
在这里循环:
foreach() / while() / for()
循环结束后,放回记录器:
$em->getConnection()->getConfiguration()->setSQLLogger($sqlLogger);
答案 4 :(得分:3)
对我而言,它正在清除学说,或正如文档所述,分离所有实体:
$this->em->clear(); //Here em is the entity manager.
所以在我的循环中,每1000次重复冲洗并分离所有实体(我不再需要它们):
foreach ($reader->getRecords() as $position => $value) {
$this->processValue($value, $position);
if($position % 1000 === 0){
$this->em->flush();
$this->em->clear();
}
$this->progress->advance();
}
希望这有帮助。
答案 5 :(得分:2)
我遇到了类似的内存泄漏问题。我在 Symfony 5.2 项目中运行 Doctrine。更具体地说,我构建了一个永无止境的命令,它处理一个表中的条目,从另一个表中检索条目,并在其他表中创建 2 个新条目。 (事件处理)
我分两步解决了我的泄漏问题。
--no-debug
(正如 Jonathan 已经建议的那样)$this->entityManager->clear();
为了查看和识别泄漏,我使用以下行输出当前内存使用情况:
$output->writeln('Memory Usage in MB: ' . memory_get_usage() / 1024 / 1024);
也许这有助于任何仍在与泄漏作斗争的人。