orm memory-leaks symfony doctrine2

orm - La memoria pierde Symfony2 Doctrine2/excede el límite de memoria



memory-leaks (8)

  1. Establecer el registrador de SQL a nulo

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

  1. Llame manualmente a la función gc_collect_cycles() después de $em->clear()

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

No olvides poner zend.enable_gc en 1, o llama manualmente a gc_enable() antes de usar gc_collect_cycles()

  1. Agregue la --no-debug si ejecuta el comando desde la consola.

en realidad tengo muchos problemas con la combinación de symfony2 y doctrine2. Tengo que lidiar con enormes conjuntos de datos (alrededor de 2-3 millones de escribir y leer) y tengo que hacer un gran esfuerzo adicional para evitar quedarse sin memoria.

Descubrí dos puntos principales, esa memoria "fuga" (en realidad no están realmente goteando, pero asignan mucho)

  1. El almacenamiento de la entidad Entitymanager (no sé el nombre real de este) parece que mantiene todas las entidades procesadas y tiene que borrar este almacenamiento de manera regular con

    $entityManager->clear()

  2. Doctrine QueryCache: almacena en caché todas las consultas utilizadas y la única configuración que encontré fue que puedes decidir qué tipo de caché quieres usar. No encontré una deshabilitación global ni una bandera útil para cada consulta para deshabilitarla. Por lo tanto, generalmente lo deshabilita para cada objeto de consulta con la función

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

entonces ... eso es todo lo que descubrí en este momento ... mis preguntas son:

¿Hay alguna manera fácil de denegar algunos objetos del Entitymanagerstorage? ¿Hay alguna manera de configurar el uso de la caché de consultas en entitymanager? ¿Puedo configurar estos comportamientos de almacenamiento en caché en alguna parte de la configuración de la doctrina de symonfony?

Sería genial si alguien tiene algunos buenos consejos para mí ... de lo contrario, esto podría ayudar a algún novato ...

cya


Acabo de publicar un montón de consejos para usar los comandos de la consola Symfony con Doctrine para el procesamiento por lotes here .


Intenta deshabilitar cualquier caché de Doctrine que exista. (Si no está usando APC / other como caché, entonces se usa memoria).

Eliminar caché de consulta

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

No hay forma de desactivarlo globalmente

También esta es una alternativa al claro que podría ayudar (desde here )

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


Intenta ejecutar tu comando con --no-debug . En el modo de depuración, el generador de perfiles retiene información sobre cada consulta en la memoria.


Según la documentación estándar de Doctrine2, deberá borrar o quitar las entidades manualmente.

Además de eso, cuando se habilita la creación de perfiles (como en el entorno de desarrollo predeterminado). El DoctrineBundle en Symfony2 configura varios registradores que usan bastante memoria. Puede deshabilitar el registro por completo, pero no es obligatorio.

Un efecto secundario interesante es que los registradores afectan tanto a Doctrine ORM como a DBAL. Uno de los registradores dará como resultado el uso de memoria adicional para cualquier servicio que use el servicio de registrador predeterminado. Deshabilitar todos estos sería ideal en comandos, ya que el generador de perfiles aún no se usa allí.

Esto es lo que puede hacer para deshabilitar los registradores intensos en memoria mientras mantiene los perfiles habilitados en otras partes de 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); }


Un poco tarde, pero creo que acabo de encontrar un hilo en Grupos de Google de Benjamin Eberlei que responde a su pregunta: como se indica en la Referencia de configuración de Doctrine, el registro predeterminado de la conexión SQL se establece en el valor de kernel.debug , por lo que si ha instanciado AppKernel con depuración establecida en verdadero, los comandos SQL se almacenan en la memoria para cada iteración.

Debe instanciar AppKernel en falso , establecer el registro en falso en su configuración YML, o bien establecer el SQLLogger manualmente en nulo antes de usar EntityManager

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


recibí algunas noticias "graciosas" de los desarrolladores de doctrinas en Symfony en vivo en Berlín - dicen, que en grandes lotes, nosotros no deberíamos usar un orm ... simplemente no es eficiente construir cosas así en oop

.. si ... tal vez tienen razón xD


1. Desactivar el registro y la creación de perfiles en app/config/config.yml

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

o en código

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

2. Forzar el recolector de basura . Si usa activamente la CPU, el recolector de basura espera y puede encontrarse sin memoria pronto.

Al principio habilite la administración manual de recolección de basura. Ejecute gc_enable() en cualquier parte del código. Luego ejecute gc_collect_cycles() para forzar el recolector de basura.

Ejemplo

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(); }

Si su tabla es muy grande, no use findAll . Use iterator - http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/batch-processing.html#iterating-results