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

时间:2012-03-14 09:32:51

标签: orm memory-leaks symfony doctrine-orm

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

我找出了2个要点,即“泄漏”内存(它们实际上并没有真正泄漏,而是分配了很多)

  1. Entitymanager实体存储(我不知道这个的实名)它似乎保留了所有处理过的entites,你必须定期清除这个存储

    $entityManager->clear()
  2. Doctrine QueryCache - 它缓存所有使用的查询,我发现的唯一配置是你能够决定你想要使用什么样的Cache。我没有发现全局禁用每个查询禁用它的有用标志。 因此通常使用函数

    为每个查询对象禁用它
    $qb = $repository->createQueryBuilder($a);
    $query = $qb->getQuery();
    $query->useQueryCache(false);
    $query->execute();
    
  3. 所以..这就是我现在想出来的所有...... 我的问题是:

    是否有一种简单的方法可以拒绝Entitymanagerstorage中的某些对象? 有没有办法在entitymanager中设置querycache? 我可以在symonfony doctrine配置中配置这种缓存行为吗?

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

    孢霉素A

8 个答案:

答案 0 :(得分:84)

有点晚了,但我想我刚刚找到Benjamin Eberlei的thread on Google Groups来回答你的问题:正如Doctrine Configuration Reference默认记录所述SQL连接设置为 kernel.debug 的值,因此如果已将调试设置为 true 的AppKernel实例化,则SQL命令将在每次迭代时存储在内存中。

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

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

答案 1 :(得分:17)

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

答案 2 :(得分:12)

<强> 1。在app/config/config.yml

中关闭记录和分析
doctrine:
    dbal:
        driver: ...
        ...
        logging: false
        profiling: false

或代码

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

<强> 2。强制垃圾收集器。如果您主动使用CPU,那么垃圾收集器会等待,您很快就会发现自己没有内存。

首先启用手动垃圾回收管理。在代码中的任何位置运行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

答案 3 :(得分:9)

  1. 将SQL记录器设置为空
  2. $em->getConnection()->getConfiguration()->setSQLLogger(null);

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

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

      请勿忘记将zend.enable_gc设置为1,或在使用gc_enable()之前手动拨打gc_collect_cycles()

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

答案 4 :(得分:4)

从学说开发人员那里得到了一些关于柏林symfony的“有趣”消息 - 他们说,在大批量生产中,我们不应该使用orm ..在oop中构建像这样的东西是没有效率的

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

答案 5 :(得分:3)

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

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

一个有趣的副作用是,记录器会影响Doctrine ORM和DBAL。其中一个记录器将导致使用默认记录器服务的任何服务的额外内存使用量。禁用所有这些在命令中是理想的 - 因为尚未在那里使用分析器。

您可以执行以下操作来禁用内存密集型记录器,同时在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);
}

答案 6 :(得分:0)

尝试禁用现有的任何Doctrine缓存。 (如果你没有使用APC / other作为缓存,则使用内存)。

删除查询缓存

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

没有办法全局禁用它

此外,这是一个可以帮助的替代方案(来自here

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

答案 7 :(得分:0)

我刚刚发布了一些使用Sytrfony控制台命令和Doctrine进行批处理的提示here