Hey, I found this quite interesting so I had a little play with it. Now, before we go any further it should be known that knowing the amount of memory a process is using isn't quite such a simple matter as it first appears. Should you, for example, include the memory being used by it's child processes or shared libraries it's making use of and a whole host of other bits and bobs? Without a deep understanding of how memory is handled between processes (something I'm afraid I don't have) the number's we get are pretty arbirtary. They do however give us a rough guide as to what's going on, in exactly the way they've been used in the previous example. I only mention this to prevent people jumping on the disparity between the results from memory_get_usage and ps it's not the point. The point is the disparity in their change.
I first tried the code out on PHP5.1.2 which doesn't appear to have memory_get_usage, not phased I wrote a little wrapper for ps to grab the RSS of the current php process and used that instead (I also changed looping a little to make lager numbers of iterations easier). No memory increase, it was all looking very nice infact.
Here's the refactored code
<?php
function mem_from_ps($pid) {
return str_replace("\n", '', shell_exec("ps -p $pid -o rss="));
}
function mem_from_top($pid) {
return shell_exec("top -b -n 1 -p $pid | head -n 5 | tail -n 2");
}
if(!function_exists('memory_get_usage')) {
function memory_get_usage() {
return 'N/A';
}
}
function print_mem($tag) {
static $pid=null;
if($pid==null) $pid = posix_getpid();
return str_pad("$tag::", 8)." ps Mem [".mem_from_ps($pid)."], php Mem [".memory_get_usage()."]";// .mem_from_top($pid);
}
@ob_end_flush(); // to be sure
function fake1( $string ){
echo "kaas ".$string;
}
function fake2( $string ){
echo "kaas $string";
}
function fake3( $string ){
$string = "kaas $string";
}
function fake4( $string ){
echo "kaas $string asdf";
}
$out = '';
for($i=1; $i<=4; $i++) {
$out .= "Testin Function No. $i\n";
for($j=0; $j<10000; $j++) {
if($j%1000==0) $out .= print_mem($j) . "\n";
call_user_func('fake'.$i, 'string');
}
}
echo("\n$out");
And here's the output using php 5.1.2
Testin Function No. 1
0:: ps Mem [ 3244], php Mem [N/A]
1000:: ps Mem [ 3300], php Mem [N/A]
2000:: ps Mem [ 3300], php Mem [N/A]
3000:: ps Mem [ 3304], php Mem [N/A]
4000:: ps Mem [ 3304], php Mem [N/A]
5000:: ps Mem [ 3304], php Mem [N/A]
6000:: ps Mem [ 3304], php Mem [N/A]
7000:: ps Mem [ 3304], php Mem [N/A]
8000:: ps Mem [ 3304], php Mem [N/A]
9000:: ps Mem [ 3304], php Mem [N/A]
Testin Function No. 2
0:: ps Mem [ 3304], php Mem [N/A]
1000:: ps Mem [ 3308], php Mem [N/A]
2000:: ps Mem [ 3308], php Mem [N/A]
3000:: ps Mem [ 3308], php Mem [N/A]
4000:: ps Mem [ 3312], php Mem [N/A]
5000:: ps Mem [ 3316], php Mem [N/A]
6000:: ps Mem [ 3316], php Mem [N/A]
7000:: ps Mem [ 3316], php Mem [N/A]
8000:: ps Mem [ 3316], php Mem [N/A]
9000:: ps Mem [ 3316], php Mem [N/A]
Testin Function No. 3
0:: ps Mem [ 3316], php Mem [N/A]
1000:: ps Mem [ 3316], php Mem [N/A]
2000:: ps Mem [ 3316], php Mem [N/A]
3000:: ps Mem [ 3316], php Mem [N/A]
4000:: ps Mem [ 3316], php Mem [N/A]
5000:: ps Mem [ 3316], php Mem [N/A]
6000:: ps Mem [ 3316], php Mem [N/A]
7000:: ps Mem [ 3316], php Mem [N/A]
8000:: ps Mem [ 3316], php Mem [N/A]
9000:: ps Mem [ 3316], php Mem [N/A]
Testin Function No. 4
0:: ps Mem [ 3316], php Mem [N/A]
1000:: ps Mem [ 3316], php Mem [N/A]
2000:: ps Mem [ 3316], php Mem [N/A]
3000:: ps Mem [ 3320], php Mem [N/A]
4000:: ps Mem [ 3320], php Mem [N/A]
5000:: ps Mem [ 3320], php Mem [N/A]
6000:: ps Mem [ 3320], php Mem [N/A]
7000:: ps Mem [ 3320], php Mem [N/A]
8000:: ps Mem [ 3320], php Mem [N/A]
9000:: ps Mem [ 3320], php Mem [N/A]
I figured it must be only be an issue with earlier versions of php so I installed php 4.4.2 and got the following results
Testin Function No. 1
0:: ps Mem [ 1804], php Mem [24416]
1000:: ps Mem [ 1828], php Mem [24640]
2000:: ps Mem [ 1828], php Mem [24712]
3000:: ps Mem [ 1828], php Mem [24776]
4000:: ps Mem [ 1828], php Mem [24840]
5000:: ps Mem [ 1828], php Mem [24904]
6000:: ps Mem [ 1828], php Mem [24968]
7000:: ps Mem [ 1828], php Mem [25032]
8000:: ps Mem [ 1828], php Mem [25096]
9000:: ps Mem [ 1828], php Mem [25160]
Testin Function No. 2
0:: ps Mem [ 1828], php Mem [25264]
1000:: ps Mem [ 1828], php Mem [29344]
2000:: ps Mem [ 1832], php Mem [29416]
3000:: ps Mem [ 1832], php Mem [29480]
4000:: ps Mem [ 1848], php Mem [29544]
5000:: ps Mem [ 1848], php Mem [29608]
6000:: ps Mem [ 1848], php Mem [29672]
7000:: ps Mem [ 1848], php Mem [29736]
8000:: ps Mem [ 1848], php Mem [29800]
9000:: ps Mem [ 1848], php Mem [29864]
Testin Function No. 3
0:: ps Mem [ 1848], php Mem [29968]
1000:: ps Mem [ 1848], php Mem [30040]
2000:: ps Mem [ 1848], php Mem [30112]
3000:: ps Mem [ 1848], php Mem [30176]
4000:: ps Mem [ 1848], php Mem [30240]
5000:: ps Mem [ 1848], php Mem [30304]
6000:: ps Mem [ 1848], php Mem [30368]
7000:: ps Mem [ 1848], php Mem [30432]
8000:: ps Mem [ 1848], php Mem [30496]
9000:: ps Mem [ 1848], php Mem [30560]
Testin Function No. 4
0:: ps Mem [ 1848], php Mem [30664]
1000:: ps Mem [ 1848], php Mem [36016]
2000:: ps Mem [ 1848], php Mem [36064]
3000:: ps Mem [ 1848], php Mem [36104]
4000:: ps Mem [ 1848], php Mem [36144]
5000:: ps Mem [ 1848], php Mem [36184]
6000:: ps Mem [ 1848], php Mem [36224]
7000:: ps Mem [ 1848], php Mem [36264]
8000:: ps Mem [ 1848], php Mem [36304]
9000:: ps Mem [ 1848], php Mem [36344]
Which is very strange because, although the memory usage according to memory_get_usage is rising stedily, according to ps it's rising at an even slower rate than with php 5.1.2. To try and get a better picture of what's going on I added in a bit to output some info from top (the overall Memory and Swap usage). The output's not so I won't post it but basically it confirmed what ps was implying, the memory usage isn't going up. I tried this with the shown number of iterations and also with 100000 iterations taking a view every 10000 iterations with exactly the same effect.
In conclusion, it looks like the bug is (if anywhere) in memory_get_usage not in the PHP core, which is reassuring. The fact that memory_get_usage seems to have been quietly dropped in more recent versions may also point to this.