内存泄漏Symfony2 Doctrine2 /超出内存限制

实际上我对symfony2和doctrine2的组合有很多麻烦。 我必须处理大量的数据集(大约2-3百万次的写入和读取),并且需要做很多额外的工作以避免内存不足。

我想出了两个要点,即“泄漏”内存(他们实际上不是真正的泄漏,而是分配很多)

  1. Entitymanager实体存储(我不知道这个真实的名字),它似乎像保持所有处理entites,你必须清除这个存储regulary

      $ entityManager->明确的() 
  2. Doctrine QueryCache – 它caching所有使用的查询,我发现唯一的configuration是你能够决定你想要使用什么types的caching。 我没有find一个全局禁用既不是一个有用的标志为每个查询来禁用它。 所以通常使用函数禁用每个查询对象

     $ qb = $ repository-> createQueryBuilder($ a);
     $ query = $ qb-> getQuery();
     $查询 - > useQueryCache(假);
     $查询 - >执行();
    

所以..这就是我现在想通了..我的问题是:

有没有简单的方法来拒绝Entitymanagerstorage的一些对象? 有没有办法在entitymanager中设置querycache的使用? 我可以在symonfony教义configuration中configuration这个caching行为吗?

如果有人对我有一些不错的提示,会很酷。否则,这可能会帮助一些菜鸟。

CYA

有点晚了,但我想我已经在 Benjamin Eberlei的Google Groups上find了一个线索,可以回答你的问题:默认情况下, Doctrine Configuration Reference指出,SQL连接的logging设置为kernel.debug的值,所以如果您已经将debugging设置为true的AppKernel实例化,则SQL命令会在每次迭代时存储在内存中。

您应该将AppKernel实例化为false ,在您configurationYML中将日志logging设置为false ,或者在使用EntityManager之前手动将SQLLogger设置为null

$em->getConnection()->getConfiguration()->setSQLLogger(null); 

尝试使用–no-debug运行您的命令。 在debugging模式下,分析器保留有关内存中每个查询的信息。

  1. 将SQLlogging器设置为空

$em->getConnection()->getConfiguration()->setSQLLogger(null);

  1. $em->clear()之后手动调用函数gc_collect_cycles() $em->clear()

$em->clear(); gc_collect_cycles();

不要忘记将zend.enable_gc设置为1,或者在使用gc_collect_cycles()之前手动调用gc_enable ()

  1. 如果从控制台运行命令,则添加--no-debug选项。

1.closures app/config/config.yml 日志logging和分析

 doctrine: dbal: driver: ... ... logging: false profiling: false 

或在代码中

 $this->em->getConnection()->getConfiguration()->setSQLLogger(null); 

2.强制垃圾收集器 。 如果你主动使用CPU,那么垃圾收集器就会等待,你很快就会发现自己没有记忆。

首先启用手动垃圾收集pipe理。 在代码的任何地方运行gc_enable() 。 然后运行gc_collect_cycles()强制垃圾收集器。

 public function execute(InputInterface $input, OutputInterface $output) { gc_enable(); // I'm initing $this->em in __construct using DependencyInjection $customers = $this->em->getRepository('AppBundle:Customer')->findAll(); $counter = 0; foreach ($customers as $customer) { // process customer - some logic here, $this->em->persist and so on if (++$counter % 100 == 0) { $this->em->flush(); // save unsaved changes $this->em->clear(); // clear doctrine managed entities gc_collect_cycles(); // PHP garbage collect // Note that $this->em->clear() detaches all managed entities, // may be you need some; reinit them here } } // don't forget to flush in the end $this->em->flush(); $this->em->clear(); gc_collect_cycles(); } 

如果你的表非常大,不要使用findAll 。 使用迭代器 – http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html#iterating-results

从柏林的symfony那里得到了一些关于开发者自己的“有趣”的消息 – 他们说,大批量的,我们不应该使用orm ..在oop中构build这样的东西是没有效率的

..是啊..也许他们是对的xD

根据标准的Doctrine2文档,您需要手动清除或分解实体。

除此之外,启用分析时(如在默认的开发环境中)。 Symfony2中的DoctrineBundleconfiguration了几个logging器使用相当多的内存。 您可以完全禁用日志logging,但不是必需的。

一个有趣的副作用是logging仪同时影响Doctrine ORM和DBAL。 logging器之一将导致使用默认logging器服务的任何服务的额外内存使用量。 禁用所有这些将是理想的命令 – 因为分析器尚未使用。

下面是你可以做什么来禁用内存密集型logging器,同时保持在Symfony2的其他部分启用分析:

 $c = $this->getContainer(); /* * The default dbalLogger is configured to keep "stopwatch" events for every query executed * the only way to disable this, as of Symfony 2.3, Doctrine Bundle 1.2, is to reinistiate the class */ $dbalLoggerClass = $c->getParameter('doctrine.dbal.logger.class'); $dbalLogger = new $dbalLoggerClass($c->get('logger')); $c->set('doctrine.dbal.logger', $dbalLogger); // sometimes you need to configure doctrine to use the newly logger manually, like this $doctrineConfiguration = $c->get('doctrine')->getManager()->getConnection()->getConfiguration(); $doctrineConfiguration->setSQLLogger($dbalLogger); /* * If profiling is enabled, this service will store every query in an array * fortunately, this is configurable with a property "enabled" */ if($c->has('doctrine.dbal.logger.profiling.default')) { $c->get('doctrine.dbal.logger.profiling.default')->enabled = false; } /* * When profiling is enabled, the Monolog bundle configures a DebugHandler that * will store every log messgae in memory. * * As of Monolog 1.6, to remove/disable this logger: we have to pop all the handlers * and then push them back on (in the correct order) */ $handlers = array(); try { while($handler = $logger->popHandler()) { if($handler instanceOf \Symfony\Bridge\Monolog\Handler\DebugHandler) { continue; } array_unshift($handlers, $handler); } } catch(\LogicException $e) { /* * As of Monolog 1.6, there is no way to know if there's a handler * available to pop off except for the \LogicException that's thrown. */ if($e->getMessage() != 'You tried to pop from an empty handler stack.') { /* * this probably doesn't matter, and will probably break in the future * this is here for the sake of people not knowing what they're doing * so than an unknown exception is not silently discarded. */ // remove at your own risk throw $e; } } // push the handlers back on foreach($handlers as $handler) { $logger->pushHandler($handler); } 

尝试禁用存在的任何Doctrinecaching。 (如果您不使用APC /其他caching,则使用内存)。

删除查询caching

 $qb = $repository->createQueryBuilder($a); $query = $qb->getQuery(); $query->useQueryCache(false); $query->useResultCache(false); $query->execute(); 

没有办法全局禁用它

此外,这是清除可能的帮助(从这里 )

 $connection = $em->getCurrentConnection(); $tables = $connection->getTables(); foreach ( $tables as $table ) { $table->clear(); } 

我刚刚发布了一些使用Symfony控制台命令和Doctrine进行批处理的技巧。