Closed
Description
Symfony version(s) affected: 5.1.0
PHP version: 7.4.6
PHPUnit version: 7.5.20
Description
Symfony leaks memory in functional tests when there's at least one access control rule set up.
How to reproduce
- Create a new Symfony project:
symfony new memory_leak_repro --full && cd memory_leak_repro
- Add an access control rule (the path is irrelevant):
sed -i '' 's/access_control:.*/access_control: [{ path: ^\/admin, roles: ROLE_ADMIN }]/g' config/packages/security.yaml
- Create a functional test:
<?php
// tests/MemoryLeakTest.php
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class MemoryLeakTest extends WebTestCase
{
public function testMemoryLeak1()
{
$client = self::createClient();
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$client->request('GET', '/admin');
$this->assertTrue(true);
}
public function testMemoryLeak2() { $this->testMemoryLeak1(); }
public function testMemoryLeak3() { $this->testMemoryLeak1(); }
public function testMemoryLeak4() { $this->testMemoryLeak1(); }
public function testMemoryLeak5() { $this->testMemoryLeak1(); }
public function testMemoryLeak6() { $this->testMemoryLeak1(); }
public function testMemoryLeak7() { $this->testMemoryLeak1(); }
public function testMemoryLeak8() { $this->testMemoryLeak1(); }
public function testMemoryLeak9() { $this->testMemoryLeak1(); }
public function testMemoryLeak10() { $this->testMemoryLeak1(); }
public function tearDown(): void
{
parent::tearDown();
print sprintf("Memory usage: %d MB\n", round(memory_get_usage() / 1024 / 1024));
}
}
- Run tests:
bin/phpunit tests
Results:
Testing tests
.Memory usage: 43 MB
.Memory usage: 48 MB
.Memory usage: 54 MB
.Memory usage: 61 MB
.Memory usage: 67 MB
.Memory usage: 74 MB
.Memory usage: 80 MB
.Memory usage: 87 MB
.Memory usage: 93 MB
Expected outcome: The memory should not increase between tests.
Possible Solution
It seems that this is caused by the container that doesn't actually free all memory when the kernel shuts down. One possible solution is to reset the container in the Kernel shutdown function.
fix.diff
:
diff --git a/src/Kernel.php b/src/Kernel.php
index 58608c7..2a15435 100644
--- a/src/Kernel.php
+++ b/src/Kernel.php
@@ -6,6 +6,7 @@ use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\Loader\Configurator\RoutingConfigurator;
+use Symfony\Contracts\Service\ResetInterface;
class Kernel extends BaseKernel
{
@@ -25,4 +26,15 @@ class Kernel extends BaseKernel
$routes->import('../config/{routes}/*.yaml');
$routes->import('../config/{routes}.yaml');
}
+
+ public function shutdown()
+ {
+ if ('test' === $this->environment && true === $this->booted) {
+ if ($this->getContainer() instanceof ResetInterface) {
+ $this->getContainer()->reset();
+ }
+ }
+
+ parent::shutdown();
+ }
}
Apply the fix:
git apply fix.diff
Run the tests again:
bin/phpunit tests
Output:
Testing tests
.Memory usage: 41 MB
.Memory usage: 46 MB
.Memory usage: 38 MB
.Memory usage: 42 MB
.Memory usage: 39 MB
.Memory usage: 36 MB
.Memory usage: 40 MB
.Memory usage: 37 MB
.Memory usage: 42 MB
Additional context
Similar issues that could be related:
- https://jolicode.com/blog/you-may-have-memory-leaking-from-php-7-and-symfony-tests - Similar bug that was fixed with [Cache] fix memory leak when using PhpArrayAdapter #34839
- [Cache] fix memory leak when using PhpArrayAdapter #34839 - memory leak due to PhpArrayAdapter
- https://bugs.php.net/bug.php?id=76982 - PHP bug (closures in included files causing memory leaks)
- https://bugs.php.net/bug.php?id=74938 - PHP bug (similar with 76982)