Project

General

Profile

Actions

Bug #89845

closed

TYPO3 Extbase: clearState not clearing much

Added by Sven Burkert over 4 years ago. Updated over 3 years ago.

Status:
Rejected
Priority:
Should have
Assignee:
-
Category:
Extbase
Target version:
-
Start date:
2019-12-04
Due date:
% Done:

0%

Estimated time:
TYPO3 Version:
10
PHP Version:
7.2
Tags:
typo3, extbase, memory
Complexity:
Is Regression:
Sprint Focus:

Description

I have an import job for scheduler written with Extbase and memory usage is really high. In TYPO3 7.6, I was able to free memory with

$this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class)->clearState();

But after upgrading to TYPO3 9.5, "clearState" seems not to work like before, this function just frees a tiny amount of memory.

I've written a simple example code which demonstrates the problem. This example can run in a Extbase Controller, no need to run it in Scheduler context.

var_dump(memory_get_usage());
$v1 = $this->frontendUserRepository->findAll();
var_dump(memory_get_usage());
foreach($v1 as $vv1) {
    $a1 = $this->frontendUserRepository->findOneByUid($vv1->getUid());
}
var_dump(memory_get_usage());
$this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class)->clearState();
var_dump(memory_get_usage());

Output is int(49190752) int(49191568) int(566560256) int(544548736): Retrieving users from the database by "findOneByUid" consumes much memory, because the objects are stored in some PHP variable and I like to free this memory. In TYPO3 7, this worked with function "clearState".


Related issues 1 (0 open1 closed)

Related to TYPO3 Core - Bug #92975: Object references are not cleared completely by using clearState - hinting at potential memory leakClosed2020-12-02

Actions
Actions #1

Updated by Susanne Moog over 4 years ago

  • Status changed from New to Needs Feedback

`clearState` did not change since 2015 - please also note that it is annotated as an internal function and not meant to be used by 3rd party code.

If you can, try to find out, what exactly is consuming that memory?

Actions #2

Updated by Sven Burkert over 4 years ago

In my example, $this->frontendUserRepository->findOneByUid() consumes the memory. 12552 records are retrieved this way, this leads to 566 MB of memory usage.

Actions #3

Updated by Alexander Schnitzler over 4 years ago

  • Status changed from Needs Feedback to Rejected

I used your code to perform manual tests and I benchmarked it with blackfire.

Maybe there is a misunderstanding in what clearState() does. It removes all references to entities inside the persistence manager and the persistence session.
This does not necessarily mean that the php garbage collection is triggered. All entities remain in the memory as long as they are used in your code.

What happens here is the following:

$v1 = $this->frontendUserRepository->findAll();

This creates a QueryResultInterface, which at this point in time does not allocate memory for entities.

foreach($v1 as $vv1) {

This triggers $v1 to instantiate all entities and it binds them in it's internal property \TYPO3\CMS\Extbase\Persistence\Generic\QueryResult::$queryResult.

$a1 = $this->frontendUserRepository->findOneByUid($vv1->getUid());

This triggers a new sql query and the instantiation of new objects. I'd consider this a bug because as long as an existing, unchanged entity exists in the session, the database should not be queried again.
As `$a1` is not outside the loop, the memory consumption only increases shortly before the garbage collection kicks in.

$this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class)->clearState();

This actually works. All references to existing objects are removed.


To have reliable numbers, you should add the following to your test snippet:

unset($v1);
var_dump(memory_get_usage());

This brings your memory consumption down to about the same level it had in the beginning.
If you prove me wrong, I will dig deeper but for now I consider this bug report invalid.

Actions #4

Updated by Sven Burkert about 4 years ago

Hi Alexander,
thank you for your input.
I added

unset($v1);var_dump(memory_get_usage());

but this didn't lower memory usage:
int(50027840) int(50028656) int(575807288) int(553503120) int(553105632)

Actions #5

Updated by Johannes Rebhan over 3 years ago

  • TYPO3 Version changed from 9 to 10

Stumbled on the same issue in an update project in TYPO3 10.

We've been using clearState to manage Extbase' memory appetite for years (since TYPO3 7) and it worked like a charm. A little trait (https://medium.com/@Waldgeist/use-a-generator-to-batch-process-typo3-repository-entries-552589b4d08d) enabled us to process 100k entries no problem, no memory hogging, nothing.

Now the same attempt to batch process feUser entries (21k of them) results in a linear memory growth, no freeing until the memory just runs out. Not PHP version dependent. This is a serious bug and issue for Extbase.

Just wrote this little testcase, which runs out of memory, hasn't before TYPO3 10.

        foreach($this->feUserRepository->findAllBatched(25) as $feUser){
            $this->output->writeln($feUser->getEmail());
            $this->output->writeln(\memory_get_usage());
        }

On the sidenote: Just declaring a function after it has been in use (and there is really no replacement for it) as @interal isn't really the way to go imho. This is a useful and vital tool in Extbase programming.

EDIT: checking the code it all seems quite simple. Too simple to fail. Haven't run that through xDebug but is it somehow possible, the DI causes multiple instance of the persistenceManager to be created instead of just a Singleton? That would maybe explain, why the clearState on the persistenceManager doesn't do anything, because every Repository has its own instance of it. But that's a pretty wild guess...

EDIT: A quick check with PHP 7.4 $ref = \WeakReference::create($feUser); shows the object is still referenced from somewhere within Extbase Framework after it should've been cleared by clearState();

        foreach($this->feUserRepository->findAllBatched(25) as $feUser){
            $ref = \WeakReference::create($feUser);
            unset($feUser);
            $this->persistenceManager->persistAll();
            $this->persistenceManager->clearState();
            $this->output->writeln("Referenced still?: " . $ref->get());
        }
Actions #6

Updated by Johannes Rebhan over 3 years ago

To make sure it's not only connected to the Symfony Command Tasks, I've tested the behaviour in a Frontend Plugin Controller Action. The same applies. A simple testcase of reading an object Model with its UID through a Repository. The result is that the reference (checked with WeakReference) still exists after clearState and unset() on the local variable.

Actions #7

Updated by Johannes Rebhan over 3 years ago

Is this maybe connected? https://forge.typo3.org/issues/60174 Is there a place where some naming error can cause multiple instance of PersistenceManager or Session to exist? Because something is clearly going wrong, when after a clearState (which clearly removes all references, freeing memory instantly) and unset the reference to an object still exists.

Please let us know if we can help to track this further.

Actions #8

Updated by Dennis Skaletz over 3 years ago

I've run into the same issue with TYPO3 10.4

Once you start working with larger imports the memory just fills up to an unreasonable amount.
While you could prevent this up to TYPO3 9.5 by making use of clearState, this does not work on the current Typo3 10.4 version.

Considering the bug has an impact on the whole PersistenceManager, it really should be fixed rather soon.
If that does not happen you will not be able to properly import large data quantities.

Actions #9

Updated by Simon Gilli over 3 years ago

  • Related to Bug #92975: Object references are not cleared completely by using clearState - hinting at potential memory leak added
Actions

Also available in: Atom PDF