Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP: Having some memory issues inside a loop

Tags:

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:

  • to unset all variables at the end of the loop (right before the sleep()).
  • Setting all variables to null.
  • encapsulating them in a separate function (while (true) { doThis(); })
  • manually calling gc_collect_cycles()

None of those worked to decrease memory usage.

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

like image 491
Pedro Cordeiro Avatar asked Aug 06 '15 18:08

Pedro Cordeiro


People also ask

What causes memory leaks in PHP?

There are several ways for memory leaks to occur. Variables that never go out of scope, cyclical references, extensions in C that `malloc` instead of `emalloc` and for whatever reason don't `free`, to name a few. There are surprising and quite subtle ways of using and holding on to memory in PHP.

Does PHP have memory management?

Because memory is allocated and released using the PHP memory management, the changes may not immediately become visible at the operating system level. The PHP memory management acts as a proxy which may delay releasing memory towards the system.

How much memory does PHP use?

While RAM is the total available memory, the memory limit is per PHP process. That means that your site can consume e.g 10 GB of RAM, with a memory limit of 256 MB. We do not limit the amount of RAM you can use, just the memory limit.


2 Answers

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).

like image 172
Pedro Cordeiro Avatar answered Sep 18 '22 15:09

Pedro Cordeiro


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

like image 23
Gustavo Straube Avatar answered Sep 16 '22 15:09

Gustavo Straube