Medium🕵️ The Memory Leak That Wasn’t: Doctrine, Cycles and PHP’s Garbage Collector | by laurentmn | Oct, 2026 | Medium
Sitemap
laurentmn

laurentmn

IT PHP/Symfony Expert, Daddy of 4 children, technology enthusiast, keen reader of books on personal development, entrepreneurship, productivity, neuro sciences.

Member-only story

🕵️ The Memory Leak That Wasn’t: Doctrine, Cycles and PHP’s Garbage Collector

8 min readOct 31, 2026

--

Every number in this article comes from a real run: PHP 8.3.6, Doctrine ORM 3.7.6, DBAL 4.5.0 and SQLite.

🕵️ The import that would not behave

You wrote an import for 100,000 orders. You flush every 500 rows and call $em->clear(). The Doctrine manual tells you to. Then a colleague adds gc_collect_cycles() after every batch. Another colleague swears that gc_disable() makes the import faster. Every team has this argument once a year, usually right after a container dies at 2 a.m. Who is right?

Neither of them can win by opinion. So we will measure. We instrument the import with memory_get_usage(true) and gc_status(), prove in a few lines that Doctrine entities form cycles that survive clear(), and compare three collector settings on the same data.

Not a Medium member yet? Click here to access this article for free!

Press enter or click to view image in full size
Illustrations generated with Adobe Firefly

🔢 How PHP frees memory

PHP counts references. Every value carries a counter. Assign the value to a variable and the counter goes up. Unset the variable and it goes down. At zero, PHP frees the value on the spot. This costs almost nothing and never pauses your script.

--

--

IT PHP/Symfony Expert, Daddy of 4 children, technology enthusiast, keen reader of books on personal development, entrepreneurship, productivity, neuro sciences.