Doctrine ORM内存问题

3
问题:
运行使用以下工厂类的Doctrine守护进程服务时,存在内存问题。当守护进程服务启动时,它占用约175MB的内存。一天后,它增加到大约250MB,再过一天就会达到400MB。我正在查找是什么原因导致内存增加以及如何降低它。
已尝试的事项:
  • $em->clear(); // 这有点帮助
  • $em->close(); // 这会导致问题
  • $em->getConnection()->getConfiguration()->setSQLLogger(null);

    --env=prod 应该正确处理setSQLLogger(null)吗?

是否有任何方法可以使用Doctrine 2.x和Symfony 2.1.x解决内存问题?
创建了一个工厂来处理连接
===================== 开始 EMFactory =====================
<?php

namespace NS\Bundle\EMBundle;

use Doctrine\ORM\EntityManager;

class EMFactory
{
    /**
     * @var
     */
    private $container;

    /**
     * @param $container
     */
    public function __construct($container)
    {
        $this->container = $container;
    }

    /**
     * @return EntityManager
     */
    public function getBlahEntityManager()
    {
        return $this->getContainer()->get('doctrine.orm.blah_manager_entity_manager');
    }

    /**
     * @return EntityManager
     */
    public function getFooEntityManager()
    {
        return $this->getContainer()->get('doctrine.orm.foo_manager_entity_manager');
    }

    /**
     * @return EntityManager
     */
    public function getBarEntityManager()
    {
        return $this->getContainer()->get('doctrine.orm.bar_manager_entity_manager');
    }

    /**
     * @return mixed
     */
    public function getContainer()
    {
        return $this->container;
    }

    /**
     * @param $container
     * @return $this
     */
    public function setContainer($container)
    {
        $this->container = $container;
        return $this;
    }

    public function closeEntityManager(EntityManager $em)
    {
        try {
            $em->clear(); // This kinda helps
            //$em->close(); // this causes issues
            //$em->getConnection()->getConfiguration()->setSQLLogger(null); // --env=prod should take care of this
        } catch (\Exception $e) {
            // exception here
        }
    }
}

我将使用一个抽象类来构建EMFactory

===================== 抽象类开始 =====================

/**
 * @param \Symfony\Component\DependencyInjection\Container $container
 */
public function __construct(Container $container)
{
    $this->container = $container;
    $this->entityManagerFactory = new EMFactory($container);
}

===================== 抽象类结束 =====================

这里有一个我如何使用EM的例子,该类继承了上面的抽象类

===================== 开始工作示例 #1 =====================

// calling like this looks to be working as expected

$fooEM = $this->getEntityManagerFactory()->getFooEntityManager();

$barResults = $fooEM->getRepository('NS\Bundle\EMBundle\Entity\Bar')->findOneBy(array('id' => 1));

if (!is_object($barResults)) {
    throw new \Exception("Bar is a non object.");
}

// some logic here ...

$this->getEntityManagerFactory()->closeEntityManager($fooEM);

===================== 工作示例 #1 结束 =====================

以下是另一个我如何使用 EM 的示例。该类扩展了上面的抽象类。

===================== 工作示例 #2 开始 =====================

// calling from functions like this

$fooEM = $this->getEntityManagerFactory()->getFooEntityManager();

$dql = 'SELECT b.*
        FROM NS\Bundle\EMBundle\Entity\Bar b            
        WHERE b.id = :id';

$query = $fooEM->createQuery($dql);
$query->setParameter('id', 1);

$barResults = $query->getResult();

$this->getEntityManagerFactory()->closeEntityManager($fooEM);

return $barResults;

===================== 结束工作示例 #2 =====================

这是我使用 EM 的另一个示例,该类继承了上面的抽象类。

===================== 开始工作示例 #3 =====================

// calling from functions like this

$fooEM = $this->getEntityManagerFactory()->getFooEntityManager();

$barEntity = new Bar();
$barEntity->setId(1);
$barEntity->setComment('this is foo-ie');

$fooEM->persist($barEntity);
$fooEM->flush();

$this->getEntityManagerFactory()->closeEntityManager($fooEM);

unset($barEntity);

===================== 结束 工作示例#3 =====================

这些只是一些基本的例子,而查询可能会变得更为复杂。

是否有任何显著的地方表明需要优化?


“Working Example #2” 所调用的任何内容是否可能无限期地保留结果? - Beau Simensen
$barResults?这是函数内的局部变量,每次调用都应该被覆盖。我也应该清除它吗?@BeauSimensen - Phill Pafford
$results = some_function(); // some_function 是你的代码,它返回 $barResults。有可能是获取结果的代码没有正确释放它们吗?我没有看到其他明显的问题。 - Beau Simensen
2个回答

1
你的问题可能来自于实例化实体管理器的方式。如果你有一个特定的实体管理器集合,你可以使用Symfony2依赖注入而不是调用容器。
每次使用访问器时,都会实例化一个新的实体管理器,因此会消耗更多的内存(而且作为守护进程,你永远不能真正释放它)。通过使用DI,你将始终拥有相同的实例。
你的EMFFactory应该像这样:
<?php

namespace NS\Bundle\EMBundle;

use Doctrine\ORM\EntityManager;

class EMFactory
{
    /**
     * @var
     */
    private $fooEm;
    /**
     * @var
     */
    private $barEm;
    /**
     * @var
     */
    private $blahEm;

    /**
     * @param $fooEm
     * @param $barEm
     * @param $blahEm
     */
    public function __construct($fooEm, $barEm, $blahEm)
    {
        $this->fooEm = $fooEm;
        $this->barEm = $barEm;
        $this->blahEm = $blahEm;
    }

    /**
     * @return EntityManager
     */
    public function getBlahEntityManager()
    {
        return $this->blahEm;
    }

    /**
     * @return EntityManager
     */
    public function getFooEntityManager()
    {
        return $this->fooEm;
    }

    /**
     * @return EntityManager
     */
    public function getBarEntityManager()
    {
        return $this->barEm;
    }

    /**
     * @return mixed
     */
    public function getContainer()
    {
        return $this->container;
    }

    /**
     * @param $container
     * @return $this
     */
    public function setContainer($container)
    {
        $this->container = $container;
        return $this;
    }

    public function closeEntityManager(EntityManager $em)
    {
        try {
            $em->clear(); // This kinda helps
            //$em->close(); // this causes issues
            //$em->getConnection()->getConfiguration()->setSQLLogger(null); // --env=prod should take care of this
        } catch (\Exception $e) {
            // exception here
        }
    }
}

然后,调整您的服务定义以将各种EM提供给您的配置,并将您的EMFactory也定义为一个服务。

我从容器中获取的原因是,如果当前的 EM 存在问题,则希望它能拉取新的 EM,否则将重用现有连接。 - Phill Pafford
当前 EM 可能会遇到什么问题?另一种避免高内存使用的方法是仍然使用容器,但将 EM 放入类变量中(有点像单例设计模式)。如果您想强制重新初始化,那么可以释放当前 EM 使用的内存,然后重新实例化一个新的 EM。但我仍然倾向于 DI 机制,这对我来说似乎更合适。 - Hugo Briand

0

这解决了我们遇到的连接问题。

只需要关闭连接即可。

public function closeEntityManager(EntityManager $em)
{
    try {
        $em->clear(); // This kinda helps
        $em->getConnection()->close(); // this seems to work            
    } catch (\Exception $e) {
        // exception here
    }
}

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