|
php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login |
[2011-01-06 15:14 UTC] jille at hexon dot cx
Description:
------------
The script shows a huge difference in memory used by PHP and memory claimed by the process. The memory is only returned to the system by exiting the process.
A simple function that would return the unused memory to the kernel would be a great solution.
Disabling gc will only make it worse.
Test script:
---------------
<?php
gc_enable();
for($i = 0; 1000000 > $i; $i++) {
$x = new stdClass();
$x->y = new stdClass();
$x->y->x = $x;
$x->meuk = str_repeat('x', 10000);
}
gc_collect_cycles();
var_dump(memory_get_usage(false));
var_dump(memory_get_usage(true));
?>
Expected result:
----------------
The usage numbers lying closer to eachother.
Actual result:
--------------
int(1816820)
int(25427968)
PatchesPull RequestsHistoryAllCommentsChangesGit/SVN commits
|
|||||||||||||||||||||||||||||||||||||
Copyright © 2001-2025 The PHP GroupAll rights reserved. |
Last updated: Thu Oct 30 20:00:01 2025 UTC |
This seems like a big problem. We are running into the same thing in our production environment. We have multiple apache servers and the memory usage continues to go up just like in the example script. We are forced to set MaxChildRequests to 10 to prevent out of memory conditions. Running top before the script, the apache/php process is taking up 13m. After running the script it says 60m. Assume you are running apache with 100 child workers and php is now taking up 6GB. I understand that for performance reasons it may be nice to keep the 60m allocated for future use but it would be nice to be able to tune this parameter. We would gladly pay the performance penalty of allocating/deallocating the memory rather than have large allocated and unused memory. However doing something like this (without circular references) works great and always frees up memory: <?php for ($i=0; $i < 20; $i++) $s = str_pad("", 1024 * 1024 * $60); ?>I understand it won't be possible to free all of the used memory, mostly due to fragmentation. Our scripts use over 100MB of memory and I don't believe every page is used. When looking at zend_alloc.c in _zend_mm_free_int: (5.3.6) if (ZEND_MM_IS_FIRST_BLOCK(mm_block) && ZEND_MM_IS_GUARD_BLOCK(ZEND_MM_BLOCK_AT(mm_block, size))) { zend_mm_del_segment(heap, (zend_mm_segment *) ((char *)mm_block - ZEND_MM_AL IGNED_SEGMENT_SIZE)); } else [...] Shouldn't that free every segment no longer used? As segments are 2MB by default, it could be possible there are some parts used in every segment, but I don't think that is very likely when running over hundreds of megabytes. If above isn't suppose to "fix my problem", would it be possible to create a function that checks whether it can remove any segments? That way the performance hit can be controlled.Is allocating memory really that much of a performance hit? It seems like allocating memory is pretty cheap. I could see a large performance hit if you were defragmenting the heap. Also something like that would be easily tunable via php.ini so users could choose if they wanted the performance penalty. We are currently working around the situation by using the following prepend file. What it does is monitor's the memory usage and tells the apache child to gracefully terminate after you get above a memory usage threshold. Sorry jille it is only useful with the apache sapi. Honestly it is a pretty ugly hack but it works... <?php function apacheMemoryUsage() { $result = 0; exec('ps -orss -p ' . getmypid(), $output); $result = trim($output[1]); return $result / 1024; } $memUseMB = apacheMemoryUsage(); $maxMem = get_cfg_var("apache_memory_limit_mb"); if (!$maxMem) $maxMem = 128; //error_log(getmypid()."> apache memory monitor: ". // "using $memUseMB MB of $maxMem MB."); if ($memUseMB > $maxMem && function_exists('posix_kill')) { error_log(getmypid()."> apache memory monitor: ". "$memUseMB MB > $maxMem MB. Sending graceful stop."); // Terminate Apache 2 child process after request has been // done by sending a SIGUSR1 POSIX signal (10) which // is a graceful stop. function killApacheChildOnExit() { error_log('posix_kill: '.getmypid()); posix_kill( getmypid(), 10 ); } register_shutdown_function( 'killApacheChildOnExit' ); } ?>