Skip to content

Memory leak in functional tests #37168

Closed
Closed
@nmrazvan

Description

@nmrazvan

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

  1. Create a new Symfony project:
symfony new memory_leak_repro --full && cd memory_leak_repro
  1. 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
  1. 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));
    }
}
  1. 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:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions