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:
unset
all variables at the end of the loop (right before the sleep()
). null
. while (true) { doThis(); }
)gc_collect_cycles()
None of those worked to decrease memory usage.
I have no idea how to force all memory to be released.
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.
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.
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.
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).
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With