Reputation: 3495
I'm experiencing problems working on a Symfony project with low memory limit. I need to create several entities, add them to another entity (entities are associated) and then flush changes to database. This happens in a while
loop.
The problem is that sometimes the loop can't finish the whole cycle because the process crashes for memory exhausting. Of course I can't raise the memory limit.
I try to explain you better with some code...
class Device
{
/**
* @ORM\ManyToMany(targetEntity="Resource", inversedBy="devices",cascade={"persist", "refresh"}, fetch="EXTRA_LAZY")
*/
protected $resources;
public function addResource(Resource $resource)
{
if ($this->resources->contains($resource)) return;
$this->resources->add($resource);
$resource->addDevice($this);
}
}
class Resource
{
/**
* @ORM\ManyToMany(targetEntity="Device", mappedBy="resources")
*/
protected $devices;
public function addDevice(Device $device)
{
if ($this->devices->contains($device)) return;
$this->devices->add($device);
$device->addResource($this);
}
}
function doIt(Device $device) {
while(...) {
$res = new Resource(...);
$device->addResource($res); // store $res in memory until end
}
$em->persist($device);
$em->flush();
}
So the problem is that until the loop finishes, $device
keeps in memory Resource
objects and sometimes memory limit is reached.
function doIt(Device $device) {
$i = 0;
while(...) {
$res = new Resource(...);
$device->addResource($res);
$i++;
if ($i % 100 == 0) {
$em->persist($device);
$em->flush($device);
$device->setResources(null); // free up Resources in memory
$em->refresh($device); // Resources are not stored in memory
}
}
$em->persist($device);
$em->flush();
}
This works not that good even because I see memory raising anyway (probably caused then by Doctrine). Moreover there are a lot of entities and several associations between them, and checking results in database gives me something wrong (e.g. an Entity not persisted).
What would you do?
Upvotes: 3
Views: 6982
Reputation: 661
I never persist more then 50 entity after flush. Try to decrease $i to 50 or less. Maybe that help.
Upvotes: 1
Reputation: 2436
There are a few things you can try to improve the situation:
Disable doctrine SQL logging mechanism in the setup stage of your import
$this->getContainer()->get('doctrine')->getConnection()->getConfiguration()->setSQLLogger(null);
Try calling a clear periodically (i do it after a batch of items is processed and flushed), but read the docs before because there may be side effects depending on your code
$this->entityManager->clear();
You already seem to be clearing all unused objects and variables you could also try calling a manual gc_collect_cycle in php
gc_collect_cycles();
If your import is executed with a symfony Command make sure you start it with the --no-debug
flag
Worked fine with an import command within my project although it didn't achieve complete memory neutrality but the remaining leaks were negligible. Maybe this can help in your case.
Upvotes: 8