I've noticed there's quite a difference between what top
or ps
reports as memory usage for a PHP process, compared to what the process itself thinks its using (with memory_get_usage
).
How much memory is the process actually using?
When running the following code along with one of my apps:
echo "Memory usage: " . pretty_bytes(memory_get_usage()) . PHP_EOL;
echo "Peak memory usage: " . pretty_bytes(memory_get_peak_usage()) . PHP_EOL;
echo "'Actual' memory usage: " . pretty_bytes(memory_get_usage(true)) . PHP_EOL;
echo "'Actual' peak memory usage: " . pretty_bytes(memory_get_peak_usage(true)) . PHP_EOL;
$ps_output = exec("ps --pid " . getmypid() . " --no-headers -o rss");
echo "'Memory usage according to ps: " . pretty_bytes(intval($ps_output) * 1000);
The output at a random point was:
Memory usage: 4.77 MB
Peak memory usage: 4.99 MB
'Actual' memory usage: 5.00 MB
'Actual' peak memory usage: 5.00 MB
Memory usage according to ps: 17.66 MB
In my particular case, this is a concern because I'm running quite a few workers and daemons.
When I set the PHP memory limit to e.g. 128 MB for each of these daemons, the processes will only get killed once they reach 128 MB according to PHP's own measurements. However, according to ps
, the processes will be using around 200 MB each by that time.
It should be emphasized what exactly the values reported by ps
and memory_get_usage(true)
are.
ps -o rss
reports the actual Resident Set Size. Relying on this value is quite a pitfall, as it does not include eventually swapped out memory. In general, you want the USS, the Unique Set Size, which is basically unshared memory (have a look at smem(8)
for that). It is the amount of unshared memory the kernel actually has mapped pages for that process, i.e. physically present unshared memory in either RAM or swapfiles. This is as close as you can get for "real" memory usage. [Also refer to /proc/$PID/smaps
for a detailed overview, as mentioned in the answer by IVO GELOV, where you could technically count the memory you want to count yourself by parsing that virtual file.]
Regarding memory_get_usage()
, this reports the heap memory actually allocated by systems using PHP's internal memory manager. This means, libraries which directly use other memory managers of the system (mmap(2)
or malloc(3)
) do not expose their memory usage here. [This is for example why mysqlnd does show much memory usage while libmysqlclient does not - the latter uses malloc()
internally.]
If you pass true
as first parameter, i.e. memory_get_usage(true)
, it returns the total amount of memory PHP's internal memory manager has asked for from the system. This number in general is slightly, but not much higher than memory_get_usage(false)
. It also is the number which the memory_limit
INI setting is compared against.
If you want to see how much workers you can run, note that PHP does not share much memory, except that the kernel may share the library memory and opcache, which shares the structures (opcodes, class info etc.). Thus the shared memory should rather not be important for you. The most important value for you thus should be the USS.
memory_get_usage
reports the memory allocated by a PHP process to run your script. ps
reports memory used by the PHP process itself, which includes the memory used for your script. PHP process uses many external libraries, which all can allocate their memory without PHP process being aware of that.
So memory_get_usage
and ps
inherently measure different things and should report different numbers. It all comes down to how do you define "actual memory usage". I understand that in your case you are more interested in the memory usage of the PHP process. Then the output of ps
is more relevant for you. But you can easily find out that even the RSS value reported by ps
is not so black and white in the world of modern operating systems and shared memories.
See also:
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