framework: Possible memory leak
- Laravel Version: 9.30.1
- PHP Version: 8.1.10
- Database Driver & Version: N/A
Description:
We’ve been having memory related issues in CI recently where the memory consumption continuously grows. This is a particular problem when enabling coverage checks (i.e. artisan test --coverage
) as this eventually exhausts the memory in the CI environment. A few weeks ago we thought we had this traced back to this issue in PHP core which was fixed as of v8.1.10, however, after updating to 8.1.10 we’re still having memory issues in CI.
I recently found this article which recommends creating a test to repeatedly create, boot and flush the (Laravel) application multiple times to see the memory consumption over time. I did this on a fresh Laravel application and, as you can see below, the memory usage constantly rises over time.
I tried to reproduce this on a personal project that doesn’t use Laravel to determine if it was an issue with phpunit or PHP core but was unable to reproduce the leak so I’m leaning towards Laravel at the moment (however, I’m not convinced of this yet).
I haven’t dug any deeper at this point as I wanted to bring this to your attention but am happy to keep investigating if necessary.
Steps To Reproduce:
Create a new Laravel application.
$ laravel new test-app
Add the following test case to the newly created application.
public function test_leaks_memory_on_1000_iterations()
{
$this->app->flush();
$this->app = null;
for ($i = 1; $i < 1000; ++$i) {
$this->createApplication()->flush();
dump('Using ' . ((int) (memory_get_usage(true) / (1024 * 1024))) . 'MB in ' . $i . ' iterations.');
}
$this->app = $this->createApplication();
}
Run the test and observe the ever increasing memory consumption.
$ phpunit tests/Feature/ExampleTest.php
PHPUnit 9.5.24 #StandWithUkraine
^ "Using 20MB in 1 iterations."
^ "Using 20MB in 2 iterations."
^ "Using 20MB in 3 iterations."
[TRUNCATED FOR BREVITY]
^ "Using 26MB in 100 iterations."
[TRUNCATED FOR BREVITY]
^ "Using 28MB in 200 iterations."
[TRUNCATED FOR BREVITY]
^ "Using 38MB in 500 iterations."
[TRUNCATED FOR BREVITY]
^ "Using 48MB in 800 iterations."
[TRUNCATED FOR BREVITY]
^ "Using 54MB in 997 iterations."
^ "Using 54MB in 998 iterations."
^ "Using 54MB in 999 iterations."
The more iterations, the more memory used.
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 2
- Comments: 40 (36 by maintainers)
@rodrigopedra 👍🏻
Going to keep working on this. For info everyone, there is a memory leak on the package bugsnag.
Maybe xdebug profiler could be helpful in this connection.
Seems this is indeed still a culprit even after a fix in PHP core was merged here: https://github.com/php/php-src/pull/9265. Also see https://github.com/laravel/framework/issues/30736
@olsavmic do you might have knowledge of any other memory leaks in PHP core?
@DarkGhostHunter I disabled that call and the memory leak still happens.
Also, I think for this to be investigated we shouldn’t focus on Opcache? Not sure what that has to do with anything.
Thank you for sharing your feedback. To keep the output of everyone focused, please only share results of real test suites without the parallel flag (not repeats, not scripts, not loops), and share the output of PHPUnit via
./vendor/bin/phpunit
(or pest with the memory plugin) like so:Going to take a look at this next Monday. ✅
I’d like to continue digging into this but not sure how to proceed (aside from a lot of code commenting). Is there an easy way to profile the code while running tests?
Adding this line:
Right after
$this->createApplication()->flush();
reduced the memory increase dramatically (from 54MB to 28MB)And commenting the last 2 bootstrapers in:
https://github.com/laravel/framework/blob/f40fe7d303544a66f7bcbee8e25c96c743d64d1a/src/Illuminate/Foundation/Console/Kernel.php#L82-L90
Further reduced from 28MB to 18MB. (in my machine it starts with 12MB)
This last one is less objective (could be any loaded service provider), and impacts the application. But at least it gives a hint on where to look further.
As discussed with @taylorotwell, we are happy with the results so far. So closing this issue.
@nunomaduro To do an apples-to-apples comparison to my previous run I used
artisan test
and dumped the memory in thetearDown()
method again this time ending up with 222 MB used in the end (down from 252 MB).I then ran it using
phpunit
directly as suggested (and will use this method going forward).This downward trend is good to see. 📉 👍
So, to keep everyone on the same page, it’s important to you’ll read this.
In what concerns memory leaks on the framework, in this issue, and previous issues regarding this topic, there are two types of reports been done by the community so far:
A. The people say that running 100,000 tests causes the memory to go from 22MB to 70MB - and keeps growing. Often, these reports are explained using things like
--repeat
orforeach
loops. B. People say that running 100 tests causes the memory to go from 22MB to hundreds of MB (or even memory exhausted).The case A is not relevant and it’s caused by two things: the peak of memory of PHP (including files and classes) and related to https://github.com/laravel/framework/pull/40656, as Laravel sets a new
set_error_handler
,set_exception_handler
,register_shutdown_function
handlers per app instance (per test). Note: setting multiple times functions likeregister_shutdown_function
is not a real-world issue, as theApplication
instance is already static and it does not leak.On this issue, it’s only important reports of the case B, again, real-world test suites, with hundreds of tests, where there is a real memory leak affecting the test suite, and that memory leak causes the memory to go from 22MB to hundreds of MB (or even memory exhausted).
Hope this helps to keep the discussion focused.
Friends, can you please try out your test suite on the latest
9.x-dev
branch, and tell me the before / after result?There is a discussion about this as well - [8.x] Memory leak on testing #39255. Might have some useful info