Description
Symfony version(s) affected: >= 2.7
Description
CachingFactoryDecorator has no bounds on its caches, leading to excessive memory usage.
One of our console commands builds several thousand form instances to generate a sitemap (the query strings are inferred from the form values).
Because the CachingFactoryDecorator holds on to every choice list ever built, the command's memory usage easily exceeds 500 MB. The problem goes away by changing the form.choice_list_factory
from form.choice_list_factory.cached
(the hard coded default) to form.choice_list_factory.default
:
services:
form.choice_list_factory: '@form.choice_list_factory.default'
How to reproduce
Run the following console command and watch the memory usage increase forever:
<?php
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
class EatMemoryCommand extends Command
{
protected function configure()
{
$this
->setName('eat-memory')
->setDescription('Consumes all available memory by repeatedly building forms')
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$formFactory = $this->getApplication()->getKernel()->getContainer()->get('form.factory');
$progress = new ProgressBar($output);
$progress->setFormat('%current% / memory usage: %memory% ');
$progress->setRedrawFrequency(1e3);
for (;;) {
$builder = $formFactory->createBuilder(FormType::class, [], []);
$builder->add('foo', ChoiceType::class, [
'choices' => [ 'bar' => 'baz', ],
]);
$builder->getForm();
$progress->advance();
}
}
}
Possible Solution
CachingFactoryDecorator should limit the lengths of its lists
and views
properties.