Possible mem leak in Query::getSingleScalarResult #6202

Closed
opened 2026-01-22 15:28:47 +01:00 by admin · 15 comments
Owner

Originally created by @flaushi on GitHub (Mar 20, 2019).

Originally assigned to: @lcobucci on GitHub.

Bug Report

My DQL query selects a number from an entity:

$query = $this->_em->createQuery("
            SELECT s.value AS value
            FROM  App\Entity\DataSample s 
            WHERE ...

");
$query->setMaxResults(1);

This query is executed tens of thousands of times inside a Symfony console application.
Now if I use getSingleScalarResult the memory consumption will explode.

try {
            $r = $query->getSingleScalarResult();
        } catch (NoResultException $e) {
            unset($e);
            $r = '0.0';
        }
        unset($query);
        return $r;

However, switching to getResult helps:

$r = $query->getResult();

        if(empty($r)){
           $r = '0.0';
        } else
            $r = $r[0]['value'];
        return $r;

In the latter case, memory consumption stays bearable.

I found this issue and developed a TestCase.

<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Annotation as ORM;
use Doctrine\ORM\NoResultException;
use Doctrine\Tests\OrmFunctionalTestCase;

/**
 * @group GH7642
 */
class GH7642Test extends OrmFunctionalTestCase
{

    private $limit = 25000;
    private $numIterations = 10000;
    /**
     * {@inheritDoc}
     */
    protected function setUp() : void
    {
        parent::setUp();

        $this->schemaTool->createSchema(
            [
                $this->em->getClassMetadata(GH7642Entity::class),
            ]
        );

    }

    private function queryResult() {
        $query = $this->em->createQuery(<<<DQL
            SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e
DQL
        );
        $query->setMaxResults(1);
        $queryResult = $query->getResult();
        if(empty($queryResult))
            return 0;
        else
            return $queryResult[0]['value'];
    }

    private function standardGetResultCase() : float {

        // iterate over results
        for ($i = 0 ; $i < $this->numIterations ; $i++){
            $this->queryResult();
        }
        return memory_get_peak_usage(true) / 1000000; // in MB

    }

    private function queryWithSingleScalarResult()  {
        $query = $this->em->createQuery(<<<DQL
            SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e
DQL
    );
        $query->setMaxResults(1);
        try {
            $r = $query->getSingleScalarResult();
        } catch (NoResultException $e){
            $r = 0;
        }
        return $r;
    }

    private function singleScalarCase() : float {

        // iterate over results
        for ($i = 0 ; $i < $this->numIterations ; $i++){
            $this->queryWithSingleScalarResult();
        }
        return memory_get_peak_usage(true) / 1000000; // in MB

    }

    public function testIssue() : void
    {

        for($i=0;$i<$this->limit;$i++) {
            $entity = new GH7642Entity(mt_rand(0,100));

            $this->em->persist($entity);
        }

        $this->em->flush();
        $this->em->clear();
        gc_collect_cycles();

        $rawQueryMemory = $this->standardGetResultCase();

        // clear all
        $this->em->flush();
        $this->em->clear();
        gc_collect_cycles();

        $namedQueryMemory = $this->singleScalarCase();

        var_dump([
            'standard'  =>$rawQueryMemory,
            'singleScalar' => $namedQueryMemory]);
        // lets assume that named query should use 2x more memory at most.
        $this->assertLessThanOrEqual($rawQueryMemory*2, $namedQueryMemory);

    }

}

/**
 * @ORM\Entity
 */
class GH7642Entity
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue
     */
    public $id;

    /**
     * @var int
     * @ORM\Column(type="decimal", precision=20, scale=4)
     */
    public $points;

    public function __construct($points)
    {
        $this->points = $points;
    }
}

Unfortunately the test case does not produce the memory leak.

However, when repeating the queries, memory consumption does increase. This should not be the case IMHO?

for ($i = 0 ; $i < 100 ; $i++) {
//            $rawQueryMemory = $this->standardGetResultCase();
// or:
            $rawQueryMemory = $this->singleScalarCase();
            var_dump($rawQueryMemory);
        }

gives

float(88.08448)
float(88.08448)
float(88.08448)
float(88.08448)
float(90.181632)
float(90.181632)
float(90.181632)
float(90.181632)
float(90.181632)
float(90.181632)
float(94.375936)
float(94.375936)
float(98.57024)
float(100.667392)
float(104.861696)
float(109.056)
float(113.250304)
float(117.444608)
float(119.54176)
float(123.736064)
float(127.930368)
float(132.124672)

Fatal error: Allowed memory size of 134217728 bytes exhausted 

One has to say that I had to switch to PHP 7.2 for the test case to compile and also pulled orm from master branch. My software uses PHP 7.1 and ORM 2.6.2

doctrine/annotations                 v1.6.0   MIT                
doctrine/cache                       v1.8.0   MIT                
doctrine/collections                 v1.5.0   MIT                
doctrine/common                      v2.10.0  MIT                
doctrine/data-fixtures               v1.3.1   MIT                
doctrine/dbal                        v2.9.2   MIT                
doctrine/doctrine-bundle             1.10.1   MIT                
doctrine/doctrine-cache-bundle       1.3.5    MIT                
doctrine/doctrine-fixtures-bundle    3.1.0    MIT                
doctrine/doctrine-migrations-bundle  v1.3.2   MIT                
doctrine/event-manager               v1.0.0   MIT                
doctrine/inflector                   v1.3.0   MIT                
doctrine/instantiator                1.1.0    MIT                
doctrine/lexer                       v1.0.1   MIT                
doctrine/migrations                  v1.8.1   MIT                
doctrine/orm                         v2.6.2   MIT                
doctrine/persistence                 v1.1.0   MIT                
doctrine/reflection                  v1.0.0   MIT   
Originally created by @flaushi on GitHub (Mar 20, 2019). Originally assigned to: @lcobucci on GitHub. ### Bug Report My DQL query selects a number from an entity: ``` $query = $this->_em->createQuery(" SELECT s.value AS value FROM App\Entity\DataSample s WHERE ... "); $query->setMaxResults(1); ``` This query is executed tens of thousands of times inside a Symfony console application. Now if I use `getSingleScalarResult` the memory consumption will explode. ``` try { $r = $query->getSingleScalarResult(); } catch (NoResultException $e) { unset($e); $r = '0.0'; } unset($query); return $r; ``` However, switching to `getResult` helps: ``` $r = $query->getResult(); if(empty($r)){ $r = '0.0'; } else $r = $r[0]['value']; return $r; ``` In the latter case, memory consumption stays bearable. I found [this issue](https://github.com/doctrine/orm/issues/7642) and developed a TestCase. ``` <?php declare(strict_types=1); namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\ORM\Annotation as ORM; use Doctrine\ORM\NoResultException; use Doctrine\Tests\OrmFunctionalTestCase; /** * @group GH7642 */ class GH7642Test extends OrmFunctionalTestCase { private $limit = 25000; private $numIterations = 10000; /** * {@inheritDoc} */ protected function setUp() : void { parent::setUp(); $this->schemaTool->createSchema( [ $this->em->getClassMetadata(GH7642Entity::class), ] ); } private function queryResult() { $query = $this->em->createQuery(<<<DQL SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e DQL ); $query->setMaxResults(1); $queryResult = $query->getResult(); if(empty($queryResult)) return 0; else return $queryResult[0]['value']; } private function standardGetResultCase() : float { // iterate over results for ($i = 0 ; $i < $this->numIterations ; $i++){ $this->queryResult(); } return memory_get_peak_usage(true) / 1000000; // in MB } private function queryWithSingleScalarResult() { $query = $this->em->createQuery(<<<DQL SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e DQL ); $query->setMaxResults(1); try { $r = $query->getSingleScalarResult(); } catch (NoResultException $e){ $r = 0; } return $r; } private function singleScalarCase() : float { // iterate over results for ($i = 0 ; $i < $this->numIterations ; $i++){ $this->queryWithSingleScalarResult(); } return memory_get_peak_usage(true) / 1000000; // in MB } public function testIssue() : void { for($i=0;$i<$this->limit;$i++) { $entity = new GH7642Entity(mt_rand(0,100)); $this->em->persist($entity); } $this->em->flush(); $this->em->clear(); gc_collect_cycles(); $rawQueryMemory = $this->standardGetResultCase(); // clear all $this->em->flush(); $this->em->clear(); gc_collect_cycles(); $namedQueryMemory = $this->singleScalarCase(); var_dump([ 'standard' =>$rawQueryMemory, 'singleScalar' => $namedQueryMemory]); // lets assume that named query should use 2x more memory at most. $this->assertLessThanOrEqual($rawQueryMemory*2, $namedQueryMemory); } } /** * @ORM\Entity */ class GH7642Entity { /** * @ORM\Id * @ORM\Column(type="integer") * @ORM\GeneratedValue */ public $id; /** * @var int * @ORM\Column(type="decimal", precision=20, scale=4) */ public $points; public function __construct($points) { $this->points = $points; } } ``` Unfortunately the test case does not produce the memory leak. However, when repeating the queries, memory consumption does increase. This should not be the case IMHO? ``` for ($i = 0 ; $i < 100 ; $i++) { // $rawQueryMemory = $this->standardGetResultCase(); // or: $rawQueryMemory = $this->singleScalarCase(); var_dump($rawQueryMemory); } ``` gives ``` float(88.08448) float(88.08448) float(88.08448) float(88.08448) float(90.181632) float(90.181632) float(90.181632) float(90.181632) float(90.181632) float(90.181632) float(94.375936) float(94.375936) float(98.57024) float(100.667392) float(104.861696) float(109.056) float(113.250304) float(117.444608) float(119.54176) float(123.736064) float(127.930368) float(132.124672) Fatal error: Allowed memory size of 134217728 bytes exhausted ``` One has to say that I had to switch to PHP 7.2 for the test case to compile and also pulled orm from master branch. My software uses PHP 7.1 and ORM 2.6.2 ``` doctrine/annotations v1.6.0 MIT doctrine/cache v1.8.0 MIT doctrine/collections v1.5.0 MIT doctrine/common v2.10.0 MIT doctrine/data-fixtures v1.3.1 MIT doctrine/dbal v2.9.2 MIT doctrine/doctrine-bundle 1.10.1 MIT doctrine/doctrine-cache-bundle 1.3.5 MIT doctrine/doctrine-fixtures-bundle 3.1.0 MIT doctrine/doctrine-migrations-bundle v1.3.2 MIT doctrine/event-manager v1.0.0 MIT doctrine/inflector v1.3.0 MIT doctrine/instantiator 1.1.0 MIT doctrine/lexer v1.0.1 MIT doctrine/migrations v1.8.1 MIT doctrine/orm v2.6.2 MIT doctrine/persistence v1.1.0 MIT doctrine/reflection v1.0.0 MIT ```
admin added the BugInvalid labels 2026-01-22 15:28:47 +01:00
admin closed this issue 2026-01-22 15:28:47 +01:00
Author
Owner

@Ocramius commented on GitHub (Mar 20, 2019):

Is the memory usage progression coming from a symfony context? Can it be reproduced without symfony (since the test case seems to deny that)?

Maybe this is related to query logging?

@Ocramius commented on GitHub (Mar 20, 2019): Is the memory usage progression coming from a symfony context? Can it be reproduced without symfony (since the test case seems to deny that)? Maybe this is related to query logging?
Author
Owner

@flaushi commented on GitHub (Mar 20, 2019):

As I pointed out, in my fuctional test case, memory does explode, too. However there it does not make a difference if I use getResult or getSingleScalarResult.

In my production code, I am only exchanging getSingleScalarResult with getResult in the repository code. In first case, 1G of memory is exhausted after 1700 iterations. With getResult, all 17000 iterations run with peak mem usage of 480 MB.

@flaushi commented on GitHub (Mar 20, 2019): As I pointed out, in my fuctional test case, memory does explode, too. However there it does not make a difference if I use `getResult` or `getSingleScalarResult`. In my production code, I am only exchanging `getSingleScalarResult` with `getResult` in the repository code. In first case, 1G of memory is exhausted after 1700 iterations. With `getResult`, all 17000 iterations run with peak mem usage of 480 MB.
Author
Owner

@flaushi commented on GitHub (Mar 20, 2019):

https://gist.github.com/flaushi/03b18152dea60bc2dba1ff405140b820
Run this test case with

php -d memory_limit=1G vendor/bin/phpunit tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php

gives:

$ php -d memory_limit=1G vendor/bin/phpunit tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php
PHPUnit 6.5.13 by Sebastian Bergmann and contributors.

Runtime:       PHP 7.1.24
Configuration: /Users/oliverdemetz/git/orm/phpunit.xml.dist

PHP Fatal error:  Allowed memory size of 1073741824 bytes exhausted (tried to allocate 20480 bytes) in /Users/oliverdemetz/git/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php on line 548
float(123.736064)
float(123.736064)
float(123.736064)
float(123.736064)
float(125.833216)
float(125.833216)
float(125.833216)
float(125.833216)
float(125.833216)
float(125.833216)
float(130.02752)
float(130.02752)
float(130.02752)
float(130.02752)
float(130.02752)
float(134.221824)
float(138.416128)
float(142.610432)
float(144.707584)
float(148.901888)
float(153.096192)
float(157.290496)
float(161.4848)
float(171.97056)
float(176.164864)
float(180.359168)
float(184.553472)
float(188.747776)
float(190.844928)
float(195.039232)
float(199.233536)
float(203.42784)
float(207.622144)
float(209.719296)
float(213.9136)
float(218.107904)
float(222.302208)
float(226.496512)
float(228.593664)
float(232.787968)
float(236.982272)
float(241.176576)
float(245.37088)
float(247.468032)
float(251.662336)
float(255.85664)
float(260.050944)
float(264.245248)
float(266.3424)
float(287.31392)
float(291.508224)
float(295.702528)
float(299.896832)
float(301.993984)
float(306.188288)
float(310.382592)
float(314.576896)
float(316.674048)
float(320.868352)
float(325.062656)
float(329.25696)
float(333.451264)
float(335.548416)
float(339.74272)
float(343.937024)
float(348.131328)
float(352.325632)
float(354.422784)
float(358.617088)
float(362.811392)
float(367.005696)
float(371.2)
float(373.297152)
float(377.491456)
float(381.68576)
float(385.880064)
float(390.074368)
float(392.17152)
float(396.365824)
float(400.560128)
float(404.754432)
float(408.948736)
float(411.045888)
float(415.240192)
float(419.434496)
float(423.6288)
float(427.823104)
float(429.920256)
float(434.11456)
float(438.308864)
float(442.503168)
float(446.697472)
float(448.794624)
float(452.988928)
float(457.183232)
float(461.377536)
float(465.57184)
float(467.668992)
float(471.863296)
float(476.0576)
float(480.251904)
float(484.446208)
float(520.097792)
float(524.292096)
float(528.4864)
float(532.680704)
float(534.777856)
float(538.97216)
float(543.166464)
float(547.360768)
float(551.555072)
float(553.652224)
float(557.846528)
float(562.040832)
float(566.235136)
float(570.42944)
float(572.526592)
float(576.720896)
float(580.9152)
float(585.109504)
float(589.303808)
float(591.40096)
float(595.595264)
float(599.789568)
float(603.983872)
float(608.178176)
float(610.275328)
float(614.469632)
float(618.663936)
float(622.85824)
float(627.052544)
float(629.149696)
float(633.344)
float(637.538304)
float(641.732608)
float(645.926912)
float(648.024064)
float(652.218368)
float(656.412672)
float(660.606976)
float(664.80128)
float(666.898432)
float(671.092736)
float(675.28704)
float(679.481344)
float(683.675648)
float(685.7728)
float(689.967104)
float(694.161408)
float(698.355712)
float(702.550016)
float(704.647168)
float(708.841472)
float(713.035776)
float(717.23008)
float(721.424384)
float(723.521536)
float(727.71584)
float(731.910144)
float(736.104448)
float(740.298752)
float(742.395904)
float(746.590208)
float(750.784512)
float(754.978816)
float(757.075968)
float(761.270272)
float(765.464576)
float(769.65888)
float(773.853184)
float(775.950336)
float(780.14464)
float(784.338944)
float(788.533248)
float(792.727552)
float(794.824704)
float(799.019008)
float(803.213312)
float(807.407616)
float(811.60192)
float(813.699072)
float(817.893376)
float(822.08768)
float(826.281984)
float(830.476288)
float(832.57344)
float(836.767744)
float(840.962048)
float(845.156352)
float(849.350656)
float(851.447808)
float(855.642112)
float(859.836416)
float(864.03072)
float(868.225024)
float(870.322176)
float(874.51648)
float(878.710784)
float(882.905088)
float(887.099392)
float(889.196544)
float(893.390848)
float(897.585152)
float(901.779456)
float(905.97376)
float(908.070912)
float(912.265216)
float(983.568384)
float(987.762688)
float(991.956992)
float(994.054144)
float(998.248448)
float(1002.442752)
float(1006.637056)
float(1010.83136)
float(1012.928512)
float(1017.122816)
float(1021.31712)
float(1025.511424)
float(1029.705728)
float(1031.80288)
float(1035.997184)
float(1040.191488)
float(1044.385792)
float(1048.580096)
float(1050.677248)
float(1054.871552)
float(1059.065856)
float(1063.26016)
float(1065.357312)
float(1069.551616)
float(1071.648768)
float(1071.648768)
float(1071.648768)
float(1071.648768)
float(1071.648768)
float(1071.648768)

Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to alloca...
@flaushi commented on GitHub (Mar 20, 2019): https://gist.github.com/flaushi/03b18152dea60bc2dba1ff405140b820 Run this test case with ``` php -d memory_limit=1G vendor/bin/phpunit tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php ``` gives: ``` $ php -d memory_limit=1G vendor/bin/phpunit tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php PHPUnit 6.5.13 by Sebastian Bergmann and contributors. Runtime: PHP 7.1.24 Configuration: /Users/oliverdemetz/git/orm/phpunit.xml.dist PHP Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to allocate 20480 bytes) in /Users/oliverdemetz/git/orm/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php on line 548 float(123.736064) float(123.736064) float(123.736064) float(123.736064) float(125.833216) float(125.833216) float(125.833216) float(125.833216) float(125.833216) float(125.833216) float(130.02752) float(130.02752) float(130.02752) float(130.02752) float(130.02752) float(134.221824) float(138.416128) float(142.610432) float(144.707584) float(148.901888) float(153.096192) float(157.290496) float(161.4848) float(171.97056) float(176.164864) float(180.359168) float(184.553472) float(188.747776) float(190.844928) float(195.039232) float(199.233536) float(203.42784) float(207.622144) float(209.719296) float(213.9136) float(218.107904) float(222.302208) float(226.496512) float(228.593664) float(232.787968) float(236.982272) float(241.176576) float(245.37088) float(247.468032) float(251.662336) float(255.85664) float(260.050944) float(264.245248) float(266.3424) float(287.31392) float(291.508224) float(295.702528) float(299.896832) float(301.993984) float(306.188288) float(310.382592) float(314.576896) float(316.674048) float(320.868352) float(325.062656) float(329.25696) float(333.451264) float(335.548416) float(339.74272) float(343.937024) float(348.131328) float(352.325632) float(354.422784) float(358.617088) float(362.811392) float(367.005696) float(371.2) float(373.297152) float(377.491456) float(381.68576) float(385.880064) float(390.074368) float(392.17152) float(396.365824) float(400.560128) float(404.754432) float(408.948736) float(411.045888) float(415.240192) float(419.434496) float(423.6288) float(427.823104) float(429.920256) float(434.11456) float(438.308864) float(442.503168) float(446.697472) float(448.794624) float(452.988928) float(457.183232) float(461.377536) float(465.57184) float(467.668992) float(471.863296) float(476.0576) float(480.251904) float(484.446208) float(520.097792) float(524.292096) float(528.4864) float(532.680704) float(534.777856) float(538.97216) float(543.166464) float(547.360768) float(551.555072) float(553.652224) float(557.846528) float(562.040832) float(566.235136) float(570.42944) float(572.526592) float(576.720896) float(580.9152) float(585.109504) float(589.303808) float(591.40096) float(595.595264) float(599.789568) float(603.983872) float(608.178176) float(610.275328) float(614.469632) float(618.663936) float(622.85824) float(627.052544) float(629.149696) float(633.344) float(637.538304) float(641.732608) float(645.926912) float(648.024064) float(652.218368) float(656.412672) float(660.606976) float(664.80128) float(666.898432) float(671.092736) float(675.28704) float(679.481344) float(683.675648) float(685.7728) float(689.967104) float(694.161408) float(698.355712) float(702.550016) float(704.647168) float(708.841472) float(713.035776) float(717.23008) float(721.424384) float(723.521536) float(727.71584) float(731.910144) float(736.104448) float(740.298752) float(742.395904) float(746.590208) float(750.784512) float(754.978816) float(757.075968) float(761.270272) float(765.464576) float(769.65888) float(773.853184) float(775.950336) float(780.14464) float(784.338944) float(788.533248) float(792.727552) float(794.824704) float(799.019008) float(803.213312) float(807.407616) float(811.60192) float(813.699072) float(817.893376) float(822.08768) float(826.281984) float(830.476288) float(832.57344) float(836.767744) float(840.962048) float(845.156352) float(849.350656) float(851.447808) float(855.642112) float(859.836416) float(864.03072) float(868.225024) float(870.322176) float(874.51648) float(878.710784) float(882.905088) float(887.099392) float(889.196544) float(893.390848) float(897.585152) float(901.779456) float(905.97376) float(908.070912) float(912.265216) float(983.568384) float(987.762688) float(991.956992) float(994.054144) float(998.248448) float(1002.442752) float(1006.637056) float(1010.83136) float(1012.928512) float(1017.122816) float(1021.31712) float(1025.511424) float(1029.705728) float(1031.80288) float(1035.997184) float(1040.191488) float(1044.385792) float(1048.580096) float(1050.677248) float(1054.871552) float(1059.065856) float(1063.26016) float(1065.357312) float(1069.551616) float(1071.648768) float(1071.648768) float(1071.648768) float(1071.648768) float(1071.648768) float(1071.648768) Fatal error: Allowed memory size of 1073741824 bytes exhausted (tried to alloca... ```
Author
Owner

@Ocramius commented on GitHub (Mar 20, 2019):

I'm referring to this bit:

Unfortunately the test case does not produce the memory leak.

Can the test be adapted to reproduce the problem?

@Ocramius commented on GitHub (Mar 20, 2019): I'm referring to this bit: > Unfortunately the test case does not produce the memory leak. Can the test be adapted to reproduce the problem?
Author
Owner

@Ocramius commented on GitHub (Mar 20, 2019):

Oh, posted at the same time. Lemme check that test case then 👍

@Ocramius commented on GitHub (Mar 20, 2019): Oh, posted at the same time. Lemme check that test case then 👍
Author
Owner

@Ocramius commented on GitHub (Mar 20, 2019):

I just went through this, and found out that the OrmFunctionalTestCase already leaks (due to static connection and SQL logger).

This is the rewritten test, which in my case doesn't leak:

<?php

declare(strict_types=1);

namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\Common\Cache\ArrayCache;
use Doctrine\DBAL\Driver\PDOSqlite\Driver;
use Doctrine\DBAL\DriverManager;
use Doctrine\ORM\Configuration;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\Tools\SchemaTool;
use PHPUnit\Framework\TestCase;

/**
 * git checkout 2.6
 * run with:
 * php -d memory_limit=1G vendor/bin/phpunit tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php
 */
class GH7642Test extends TestCase
{
    private $limit = 250;

    private $numIterations = 100;

    /** @var int */
    private $initialMemoryUsage = 0;

    /** @var EntityManagerInterface */
    private $entityManager;

    /**
     * {@inheritDoc}
     */
    protected function setUp() : void
    {
        parent::setUp();

        $config = new Configuration();
        $config->setMetadataCacheImpl(new ArrayCache());
        $config->setQueryCacheImpl(new ArrayCache());
        $config->setProxyDir(__DIR__ . '/../../../Proxies');
        $config->setProxyNamespace('Doctrine\Tests\Proxies');
        $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver(__FILE__));

        $connection = DriverManager::getConnection(
            [
                'driverClass' => Driver::class,
                'memory'      => true,
            ],
            $config
        );

        $this->entityManager = EntityManager::create($connection, $config);

        (new SchemaTool($this->entityManager))->createSchema([
            $this->entityManager->getClassMetadata(GH7642Entity::class),
        ]);

        gc_collect_cycles();
        $this->initialMemoryUsage = memory_get_usage();
    }

    private function queryResult()
    {
        $query = $this->entityManager->createQuery(<<<DQL
            SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e
DQL
        );
        $query->setMaxResults(1);
        $queryResult = $query->getResult();
        if (empty($queryResult)) {
            return 0;
        } else {
            return $queryResult[0]['value'];
        }
    }

    private function standardGetResultCase() : int
    {
        // iterate over results
        for ($i = 0; $i < $this->numIterations; $i++) {
            $this->queryResult();
        }
        gc_collect_cycles();
        return memory_get_usage();
    }

    private function queryWithSingleScalarResult()
    {
        $query = $this->entityManager->createQuery(<<<DQL
            SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e
DQL
        );
        $query->setMaxResults(1);
        try {
            $r = $query->getSingleScalarResult();
        } catch (NoResultException $e) {
            $r = 0;
        }
        return $r;
    }

    private function singleScalarCase() : int
    {
        // iterate over results
        for ($i = 0; $i < $this->numIterations; $i++) {
            $this->queryWithSingleScalarResult();
        }
        return memory_get_usage();
    }

    public function testIssue() : void
    {
        for ($i = 0; $i < $this->limit; $i++) {
            $entity = new GH7642Entity(mt_rand(0, 100));
            $this->entityManager->persist($entity);
        }
        $this->entityManager->flush();
        $this->entityManager->clear();
        gc_collect_cycles();
        for ($i = 0; $i < $this->numIterations; $i++) {
            $rawQueryMemory = $this->standardGetResultCase();
//            $rawQueryMemory = $this->singleScalarCase();
            var_dump($rawQueryMemory - $this->initialMemoryUsage);
        }
        // clear all
        $this->entityManager->flush();
        $this->entityManager->clear();
        gc_collect_cycles();
        $namedQueryMemory = $this->singleScalarCase();
        var_dump([
            'standard'     => $rawQueryMemory - $this->initialMemoryUsage,
            'singleScalar' => $namedQueryMemory - $this->initialMemoryUsage]);
        // lets assume that named query should use 2x more memory at most.
        $this->assertLessThanOrEqual($rawQueryMemory * 2, $namedQueryMemory);
    }
}

/**
 * @Entity
 */
class GH7642Entity
{
    /**
     * @Id
     * @Column(type="integer")
     * @GeneratedValue
     */
    public $id;

    /**
     * @var int
     * @Column(type="decimal", precision=20, scale=4)
     */
    public $points;

    public function __construct($points)
    {
        $this->points = $points;
    }
}

Produces:

/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128:
int(2212880)
/home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:137:
array(2) {
  'standard' =>
  int(2212880)
  'singleScalar' =>
  int(2221776)
}

Note how I use memory_get_usage(), not memory_get_peak_usage(), since PHPUnit also adds overhead.

Can you check if the modified test is flawed or not?

@Ocramius commented on GitHub (Mar 20, 2019): I just went through this, and found out that the `OrmFunctionalTestCase` already leaks (due to static connection and SQL logger). This is the rewritten test, which in my case doesn't leak: ```php <?php declare(strict_types=1); namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\Common\Cache\ArrayCache; use Doctrine\DBAL\Driver\PDOSqlite\Driver; use Doctrine\DBAL\DriverManager; use Doctrine\ORM\Configuration; use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\NoResultException; use Doctrine\ORM\Tools\SchemaTool; use PHPUnit\Framework\TestCase; /** * git checkout 2.6 * run with: * php -d memory_limit=1G vendor/bin/phpunit tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php */ class GH7642Test extends TestCase { private $limit = 250; private $numIterations = 100; /** @var int */ private $initialMemoryUsage = 0; /** @var EntityManagerInterface */ private $entityManager; /** * {@inheritDoc} */ protected function setUp() : void { parent::setUp(); $config = new Configuration(); $config->setMetadataCacheImpl(new ArrayCache()); $config->setQueryCacheImpl(new ArrayCache()); $config->setProxyDir(__DIR__ . '/../../../Proxies'); $config->setProxyNamespace('Doctrine\Tests\Proxies'); $config->setMetadataDriverImpl($config->newDefaultAnnotationDriver(__FILE__)); $connection = DriverManager::getConnection( [ 'driverClass' => Driver::class, 'memory' => true, ], $config ); $this->entityManager = EntityManager::create($connection, $config); (new SchemaTool($this->entityManager))->createSchema([ $this->entityManager->getClassMetadata(GH7642Entity::class), ]); gc_collect_cycles(); $this->initialMemoryUsage = memory_get_usage(); } private function queryResult() { $query = $this->entityManager->createQuery(<<<DQL SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e DQL ); $query->setMaxResults(1); $queryResult = $query->getResult(); if (empty($queryResult)) { return 0; } else { return $queryResult[0]['value']; } } private function standardGetResultCase() : int { // iterate over results for ($i = 0; $i < $this->numIterations; $i++) { $this->queryResult(); } gc_collect_cycles(); return memory_get_usage(); } private function queryWithSingleScalarResult() { $query = $this->entityManager->createQuery(<<<DQL SELECT e.points AS value FROM Doctrine\Tests\ORM\Functional\Ticket\GH7642Entity e DQL ); $query->setMaxResults(1); try { $r = $query->getSingleScalarResult(); } catch (NoResultException $e) { $r = 0; } return $r; } private function singleScalarCase() : int { // iterate over results for ($i = 0; $i < $this->numIterations; $i++) { $this->queryWithSingleScalarResult(); } return memory_get_usage(); } public function testIssue() : void { for ($i = 0; $i < $this->limit; $i++) { $entity = new GH7642Entity(mt_rand(0, 100)); $this->entityManager->persist($entity); } $this->entityManager->flush(); $this->entityManager->clear(); gc_collect_cycles(); for ($i = 0; $i < $this->numIterations; $i++) { $rawQueryMemory = $this->standardGetResultCase(); // $rawQueryMemory = $this->singleScalarCase(); var_dump($rawQueryMemory - $this->initialMemoryUsage); } // clear all $this->entityManager->flush(); $this->entityManager->clear(); gc_collect_cycles(); $namedQueryMemory = $this->singleScalarCase(); var_dump([ 'standard' => $rawQueryMemory - $this->initialMemoryUsage, 'singleScalar' => $namedQueryMemory - $this->initialMemoryUsage]); // lets assume that named query should use 2x more memory at most. $this->assertLessThanOrEqual($rawQueryMemory * 2, $namedQueryMemory); } } /** * @Entity */ class GH7642Entity { /** * @Id * @Column(type="integer") * @GeneratedValue */ public $id; /** * @var int * @Column(type="decimal", precision=20, scale=4) */ public $points; public function __construct($points) { $this->points = $points; } } ``` Produces: ``` /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:128: int(2212880) /home/ocramius/Documents/doctrine/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7642Test.php:137: array(2) { 'standard' => int(2212880) 'singleScalar' => int(2221776) } ``` Note how I use `memory_get_usage()`, not `memory_get_peak_usage()`, since PHPUnit also adds overhead. Can you check if the modified test is flawed or not?
Author
Owner

@flaushi commented on GitHub (Mar 20, 2019):

I can confirm that the modified test does not leak memory.
Still, I definitely see the described behavior in my console application (no debug, no sqlLogger and no second level cache logger).
Definitely, the one and only difference is my repository method:

       if($this->_em->getConfiguration()->getSQLLogger() !== null)
            throw new \Exception('SQL Logger is non-zero');

        if($this->_em->getConfiguration()->getSecondLevelCacheConfiguration()->getCacheLogger() !== null)
            throw new \Exception('SLC Logger is non-zero');

        /**
         * This code does not leak:
         */
        /*
        $r = $query->getResult();
        if(empty($r)){
            if($w->intervalConfined)
                $r = '0.0';
            else
                $r = 'no value';
        } else
            $r = $r[0]['value'];
        return $r;
        */
        /**
         * This code does leak:
         */
        try {
            $r = $query->getSingleScalarResult();
        } catch (NoResultException $e) {
            unset($e);
            if($w->intervalConfined)
                $r = '0.0';
            else
                $r = 'no value';
        }
        unset($query);
        return $r;
@flaushi commented on GitHub (Mar 20, 2019): I can confirm that the modified test does not leak memory. Still, I definitely see the described behavior in my console application (no debug, no sqlLogger and no second level cache logger). Definitely, the **one and only** difference is my repository method: ``` if($this->_em->getConfiguration()->getSQLLogger() !== null) throw new \Exception('SQL Logger is non-zero'); if($this->_em->getConfiguration()->getSecondLevelCacheConfiguration()->getCacheLogger() !== null) throw new \Exception('SLC Logger is non-zero'); /** * This code does not leak: */ /* $r = $query->getResult(); if(empty($r)){ if($w->intervalConfined) $r = '0.0'; else $r = 'no value'; } else $r = $r[0]['value']; return $r; */ /** * This code does leak: */ try { $r = $query->getSingleScalarResult(); } catch (NoResultException $e) { unset($e); if($w->intervalConfined) $r = '0.0'; else $r = 'no value'; } unset($query); return $r; ```
Author
Owner

@flaushi commented on GitHub (Mar 20, 2019):

I also wrapped the line

$r = $query->getResult();

in a try-catch to check if this produces the leak, but no.

@flaushi commented on GitHub (Mar 20, 2019): I also wrapped the line ``` $r = $query->getResult(); ``` in a try-catch to check if this produces the leak, but no.
Author
Owner

@Ocramius commented on GitHub (Mar 20, 2019):

Since we can exclude ORM from being the root cause here, I think you should investigate and see what the ORM configuration (EntityManager#getConfiguration() and Connection#getConfiguration()) yield. Possibly a framework dependency collecting things over time.

@Ocramius commented on GitHub (Mar 20, 2019): Since we can exclude ORM from being the root cause here, I think you should investigate and see what the ORM configuration (`EntityManager#getConfiguration()` and `Connection#getConfiguration()`) yield. Possibly a framework dependency collecting things over time.
Author
Owner

@flaushi commented on GitHub (Mar 20, 2019):

Yeah I also thought that's it but couldn't find anything.

I am irritated about the fact that getSingleScalarResult is the reproducible trigger...

@flaushi commented on GitHub (Mar 20, 2019): Yeah I also thought that's it but couldn't find anything. I am irritated about the fact that `getSingleScalarResult` is the reproducible trigger...
Author
Owner

@Ocramius commented on GitHub (Mar 20, 2019):

Maybe this is related to some listeners?

@Ocramius commented on GitHub (Mar 20, 2019): Maybe this is related to some listeners?
Author
Owner

@flaushi commented on GitHub (Mar 20, 2019):

I do have listeners, but only pre/posPersist/Update/Flush. This command does not issue any $em->flush() or persist so none of them should be called.
I'll try a MySQL connection now...

@flaushi commented on GitHub (Mar 20, 2019): I do have listeners, but only pre/posPersist/Update/Flush. This command does not issue any `$em->flush()` or `persist` so none of them should be called. I'll try a MySQL connection now...
Author
Owner

@flaushi commented on GitHub (Mar 20, 2019):

I can't believe it. It seems to be the PostgreSQL connection.
This explains why the TestCase did work: it's a SQLite in-memory db.

I uploaded a 100 % equal data set to a Postgres and MySQL connection.
On MySQL the memory consumption is the same for both getResult and getSingleScalarResult,

on Postgresql the getSingleScalarResult version has increasing memory consumption while the getResult is clean. Incredible.

@flaushi commented on GitHub (Mar 20, 2019): I can't believe it. It seems to be the PostgreSQL connection. This explains why the TestCase did work: it's a SQLite in-memory db. I uploaded a 100 % equal data set to a Postgres and MySQL connection. On MySQL the memory consumption is the same for both `getResult` and `getSingleScalarResult`, on Postgresql the `getSingleScalarResult` version has increasing memory consumption while the `getResult` is clean. Incredible.
Author
Owner

@lcobucci commented on GitHub (Oct 2, 2019):

@flaushi @Ocramius shall we close this one?

@lcobucci commented on GitHub (Oct 2, 2019): @flaushi @Ocramius shall we close this one?
Author
Owner

@flaushi commented on GitHub (Oct 2, 2019):

Yes, I did not re-encounter the problem since then. Thanks for your support!

@flaushi commented on GitHub (Oct 2, 2019): Yes, I did not re-encounter the problem since then. Thanks for your support!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#6202