PHP/Symfony/Doctrine 内存泄漏?

16
我使用Symfony 1.4和Doctrine 1.2批量将对象插入数据库时遇到问题。我的模型有一种名为“Sector”的对象,每个对象都有几个类型为“Cupo”的对象(通常在50至200000之间)。这些对象非常小,只包含一个短的标识符字符串和一两个整数。每当用户创建一组Sectors时,我需要自动将所有这些“Cupo”实例添加到数据库中,并且在出现任何问题时,我会使用Doctrine事务回滚所有更改。但问题是,在php耗尽内存之前,我只能创建约2000个实例。内存限制目前为128MB,应足以处理使用少于100字节的对象。我已尝试将内存限制增加到512MB,但php仍然崩溃,这并没有解决问题。我是否正在正确地进行批处理插入,还是有更好的方法?以下是错误信息:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 71 bytes) in /Users/yo/Sites/grifoo/lib/vendor/symfony/lib/log/sfVarLogger.class.php on line 170

这里是代码:

public function save($conn=null){

    $conn=$conn?$conn:Doctrine_Manager::connection();

    $conn->beginTransaction();


    try {
        $evento=$this->object;


        foreach($evento->getSectores() as $s){

            for($j=0;$j<$s->getCapacity();$j++){

                $cupo=new Cupo();
                $cupo->setActivo($s->getActivo());
                $cupo->setEventoId($s->getEventoId());
                $cupo->setNombre($j);
                $cupo->setSector($s);

                $cupo->save();

            }
        }

        $conn->commit();
        return;
    }
    catch (Exception $e) {
        $conn->rollback();
        throw $e;
    }

再次说一遍,这段代码对于少于1000个对象工作正常,但是超过1500个对象则失败。感谢帮助。


1
把某人的答案标记为正确的,好吗? - Andrei Dziahel
对于批量插入,您可能还想考虑通过PDO进行原始SQL插入,这肯定会更快,并且不会泄漏内存。 - John Carter
9个回答

35

尝试做:

$cupo->save();
$cupo->free();
$cupo = null;

(但是用我的代码替换后),我仍然遇到内存溢出的问题。还有其他想法吗,SO?

更新:

我在databases.yml中创建了一个新的环境,它看起来像:

all:
  doctrine:
    class: sfDoctrineDatabase
    param:
      dsn: 'mysql:host=localhost;dbname=.......'
      username: .....
      password: .....
      profiler: false

分析器: false 条目禁用了Doctrine的查询日志记录,它通常会保存您执行的每个查询的副本。虽然它没有阻止内存泄漏,但我能够比之前多处理约两倍的数据导入。

更新2

我添加了

Doctrine_Manager::connection()->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true ); 

在运行我的查询之前,我做出了改变。

$cupo = null;

unset($cupo);

现在,我的脚本一直在愉快地运行。我非常确信这次它会顺利完成而不会耗尽RAM。

更新3

是的,这就是获胜的组合。


1
我遇到了类似的问题。我尝试了这些方法,我的性能显著提高了。 - Jason Swett
如果可能的话,请升级到php5.3并使用->free()。内存泄漏是由于PHP5.2的垃圾收集器无法清除不再从其循环外部引用的循环引用引起的。PHP5.3具有更好的收集器,而free()则尝试在变量离开作用域之前撤消这些引用。 - Jordan Feldstein
1
@Jordan - 哇,我刚遇到一个类似的问题,但是在 select 上,profiler: false 产生了巨大的影响...谢谢 - Manse
1
+1 对于性能分析器的提示。它严重影响了我的应用程序。添加了profiler:false后,即使有成千上万条记录,峰值使用率也几乎没有变化。 - lopsided
FYI,在生产环境中保持分析器启用并不是一个好的建议。 - Aman

3

我也遇到了这个问题,为了解决symfony任务需要做以下几件事情,这对我很有效:

  • Disable debug mode. Add following before db connection initialize

    sfConfig::set('sf_debug', false);
    
  • Set auto query object free attribute for db connection

    $connection->setAttribute(Doctrine_Core::ATTR_AUTO_FREE_QUERY_OBJECTS, true );
    
  • Free all object after use

    $object_name->free()
    
  • Unset all arrays after use unset($array_name)

  • Check all doctrine queries used on task. Free all queries after use. $q->free() (This is a good practice for any time of query using.)

就这些了。希望能对某些人有所帮助。


3

我刚刚使用Symfony 1.4编写了“守护进程”脚本,并设置了以下内容,以停止内存占用:

sfConfig::set('sf_debug', false);

如果在创建数据库管理器之前执行此操作,它对我有效。如果您正在执行任务,则认为这是安全的。 - Alex Grin

2

Doctrine存在泄漏问题,这是无法避免的。确保您在适用的情况下使用 $q->free() 来最小化影响。

Doctrine不适用于维护脚本。解决此问题的唯一方法是将脚本拆分为执行部分任务的几个部分。一种方法是向脚本添加一个起始参数,并在处理了一定数量的对象后,将脚本重定向到具有更高起始值的自身。尽管这使得编写维护脚本更加繁琐,但对我而言效果很好。


我有过同样的经历,所有那些小技巧并没有真正帮助。我概述了与您类似的方法: https://dev59.com/zFfUa4cB1Zd3GeqPEhz7#11474869 - Tapper

1
尝试使用以下方法打破通常会导致内存泄漏的循环引用:
$cupo->save();

$cupo->free(); //this call

如Doctrine手册中所描述


1

对我来说,我只是这样初始化任务:

// initialize the database connection
$databaseManager = new sfDatabaseManager($this->configuration);
$connection = $databaseManager->getDatabase($options['connection'])->getConnection();
$config = ProjectConfiguration::getApplicationConfiguration('frontend', 'prod', true);
sfContext::createInstance($config);

(使用生产配置)
在Doctrine对象的save()之后使用free()函数。
内存稳定在25Mo。
memory_get_usage=26.884071350098Mo

在Debian Squeeze上使用PHP 5.3


1
尝试在每次保存后使用 unset($cupo);。这应该会有所帮助。另一件事是拆分脚本并进行一些批处理。

0

对我有效的方法是这样调用free方法:

$cupo->save();
$cupo->free(true); // free also the related components
unset($cupo);

0
定期关闭并重新打开连接-不确定为什么,但似乎PDO保留了引用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接