11

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.

hansmaad
  • 18,417
  • 9
  • 53
  • 94
Robbert
  • 5,063
  • 6
  • 36
  • 44

3 Answers3

6

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:

Community
  • 1
  • 1
Erki Aring
  • 2,032
  • 13
  • 15
  • Thanks Erki. This was a helpful start, but we were still missing some level of detail that @bwoebi was able to provide in his answer. – Robbert Aug 02 '16 at 15:14
6

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.

bwoebi
  • 23,637
  • 5
  • 58
  • 79
  • Thanks @bwoebi. This and Ivo's answer have given us a sufficient level of insight into the problem to tackle the issue at hand. – Robbert Aug 02 '16 at 15:15
2

You may find interesting things when issue one of these commands:

cat /proc/PID_NUMBER/smaps
pmap -d PID_NUMBER
IVO GELOV
  • 13,496
  • 1
  • 17
  • 26
  • 1
    Some secret outcome without an interpretation (although potentially educating) is probably not the right approach for SO. ;o) – Jakumi Aug 02 '16 at 12:08
  • that might very well be, but unless you link to some of those many places, your answer doesn't provide any insights by itself. – Jakumi Aug 02 '16 at 12:29
  • Sorry - timed out the 5 minutes :( http://man7.org/linux/man-pages/man5/proc.5.html, http://unix.stackexchange.com/questions/33381/getting-information-about-a-process-memory-usage-from-proc-pid-smaps, http://bmaurer.blogspot.bg/2006/03/memory-usage-with-smaps.html, https://techtalk.intersec.com/2013/07/memory-part-2-understanding-process-memory, https://jameshunt.us/writings/smaps.html, https://utcc.utoronto.ca/~cks/space/blog/linux/SmapsFields, https://lwn.net/Articles/230975/, http://superuser.com/a/888245 – IVO GELOV Aug 02 '16 at 12:34
  • Though not the most detailed answer, this did actually prove useful in figuring out where the memory usage is coming from. We were able to determine from these commands that at the very least the PHP modules we're using are not leaking memory. Thank you Ivo. – Robbert Aug 02 '16 at 15:13