Pedro Cordeiro
Pedro Cordeiro

Reputation: 2125

How do I resolve memory issues inside a loop

I have a snippet that resembles the following:

while (true) {
    $myObject = new Class();
    $myOtherObject = $myObject->getSomeOtherObj();
    ...
    $myArray = [1, 2, 3];
    ...
    sleep(1); //Sleep at the end, to save CPU.
}

This snippet should run as a daemon service, but I'm having a lot of trouble making this work.

The issue: each iteration increases the process memory usage. As if at each new iteration a new $myObject is being instantiated, but the previous one remains allocated in memory, and such.

I have tried:

None of those worked to decrease memory usage.

I have no idea how to force all memory to be released.

Upvotes: 23

Views: 13844

Answers (6)

Gustavo Straube
Gustavo Straube

Reputation: 3871

I'm compiling my previous comments in an answer here. This doesn't explain exactly how you can free allocated memory, but it will guide you through a way to discover what in your application is causing that. With that, you can work on optimizing your code.

Finding memory usage bottlenecks is usually a challenging task. You can start by looking at your I/O-related calls, like database queries, file access, or even networking. Beyond increasing the execution time, sometimes these operations can allocate some amount of memory.

If you're already removing from memory the resources returned by I/O operations and no considerable decrease in allocated memory is noticed, the next step might be profiling your application using a tool like Blackfire (https://blackfire.io/).

Blackfire will give you a detailed view of each function call and its statistics on memory, CPU, and execution time. With that data, it's possible to check which operations are allocating excessive memory. You can find this info when you land your mouse pointer over the memory bar inside the call details, like this:

Allocated memory

Upvotes: 10

Pedro Cordeiro
Pedro Cordeiro

Reputation: 2125

After much research on the topic, I am finally convinced that there are no ways to manually force the memory to be released or to force object destruction.

However, something that has helped me lower the memory usage (absolutely preventing infinite memory stacking was not possible) was to realize that there are no loop scopes in PHP and that the Garbage Collection happens when switching scopes.

In C# or Java, a variable created within a while (...) {} is only accessible from within the loop. This is not the norm for PHP. A $myObject created from within a while instruction is accessible throughout your entire application.

This means the provided snippet would be better presented as:

while (true) {
    myFunc();
}

function myFunc()
{
    $myObject = new Class();
    $myOtherObject = $myObject->getSomeOtherObj();
    ...
    $myArray = [1, 2, 3];
    ...
    sleep(1); //Sleep at the end, to save CPU.
}

Encapsulating the logic in a function forces the scope to change, which means the Garbage Collector will be called at each iteration. This has not solved my problem, but it has lowered my RAM usage somewhat.

What I have learned from this experience is that PHP is probably not suited to this specific project requirement. I'd need more control over memory, and PHP doesn't provide any kind of control over created/destroyed objects. Some native functions do not release memory properly (specially those that do I/O, database access and memcached).

Upvotes: 15

KimvdLinde
KimvdLinde

Reputation: 617

The problem is that you are in an infinite loop, with no end at the request. The garbage collector of PHP is designed to be dealt with at the end of the request, and it not accessible to the user otherwise. PHP is designed to be called and discarded, not to be kept alive indefinately. Hence it is not fixable. So, what I would suggest is to create a chron job that restarts the php loop at regular intervals thus ending the request and freeing up the memory. See this document for details.

Upvotes: 2

Mahdi
Mahdi

Reputation: 9427

As both @m1lt0n and @MartPluijmaekers has mention above, this might be an issue related to object references.

We don't know what is inside your Class() class and getSomeOtherObj() method, so I can't say anything for sure, however below snippet might be able to help you figure out if that is the case or not.

/* Here is your Class */
class Class {

    public function __construct () {
        $this->child = new AnotherClass( $this );
    }

    /* This is what you have to have ... */
    protected function __destruct () {
        unset( $this->child );
    }
}

/* Here is the other class  */
class AnotherClass {

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

/* Infinite Loop */
while ( true ) {

    $myObject = new Class();
    $myOtherObject = $myObject->getSomeOtherObj();

    $myArray = [1, 2, 3];

    /* manually destroying the object */
    $myObject->__destruct();
    unset( $myObject );

    /* rest of free-ing goes here ... */

    sleep(1); //Sleep at the end, to save CPU.
}

The snippet should be pretty much self-explanatory.

Upvotes: 0

m1lt0n
m1lt0n

Reputation: 361

My best guess (due to lack of knowledge of the internals of the Classes involved) is that either the classes assign other Objects as their properties (or perhaps they have self-references) or that the array used (since the code sample resembles the real case) has a reference to itself, which would explain the memory leak if the size of the array is significant.

If it is of any help, please check out the reference counting fundamentals from php.net:

http://php.net/manual/en/features.gc.refcounting-basics.php

Upvotes: 1

Mart Pluijmaekers
Mart Pluijmaekers

Reputation: 66

It is very likely (provided with the information given) that there are still references to the created object which prevent the garbage collector from removing the object from memory. Since it is basically counting references, therefor making sure that no reference is being stored, by either making copies of values or unsetting them carefully this can be avoided.

Normally it is easier when using while(true) constructs to not create objects precisely for this reason and make it as self contained as possible just to make sure that no memory leaks can actually happen.

I know this answer is not very helpfull in a direct manner (and I do not have enough rep to comment on the question) but it might get you on the right track.

Upvotes: 2

Related Questions