When dealing with memory leaks in Apache/PHP environments, the first step is validating the leak pattern. In our case with Drupal 6.x on RHEL 5.6, we observed:
- Steady memory growth despite stable Apache process count
- Memory release upon
httpd reload
- 512MB
memory_limit
per process being consumed over time
# Typical memory observation commands: watch -n 5 'ps -ylC httpd --sort:rss | awk \'{sum+=$8; ++n} END {print "Tot="sum"("n") Avg="sum/n" Max="$8}\'' free -m
For PHP 5.3 environments, these tools prove most effective:
1. PHP Memory Tracking
<?php // Add to settings.php function track_memory() { static $max = 0; $current = memory_get_usage(true); if ($current > $max) { $max = $current; error_log(sprintf("Memory peak: %.2fMB", $max/1048576)); } } register_shutdown_function('track_memory'); ?>
2. Apache Process Inspection
# Monitor process memory changes: sudo apt-get install smem smem -P httpd -s rss -k -c "pid rss pss swap command"
From experience, these Drupal-specific patterns often cause leaks:
// Typical leak pattern in custom modules: function hook_init() { // Static variables persist across requests static $heavy_data = []; if (empty($heavy_data)) { $heavy_data = some_expensive_operation(); } }
For PHP 5.3, the older XHProf works better than XHProfNG:
pecl install xhprof-beta echo 'extension=xhprof.so' > /etc/php5/conf.d/xhprof.ini
Sample profiling code:
<?php xhprof_enable(XHPROF_FLAGS_MEMORY); register_shutdown_function(function() { $data = xhprof_disable(); file_put_contents('/tmp/xhprof_'.uniqid().'.log', serialize($data)); }); ?>
Critical php.ini settings for leak investigation:
; Recommended debug settings memory_limit = 512M max_execution_time = 120 display_errors = On log_errors = On error_log = /var/log/php_errors.log
Create a monitoring script like:
#!/bin/bash while true; do TIMESTAMP=$(date +%Y%m%d_%H%M%S) ps -eo pid,rss,comm | grep httpd > /var/log/httpd_mem_$TIMESTAMP.log lsof -p $(pgrep httpd) >> /var/log/httpd_files_$TIMESTAMP.log sleep 300 done
Implement these Drupal 6.x performance tweaks:
// In settings.php $conf['cache'] = 1; $conf['page_compression'] = 0; $conf['preprocess_css'] = 1; $conf['preprocess_js'] = 1;
When dealing with Apache/PHP memory leaks in Drupal environments, the first step is establishing clear monitoring. The memory growth pattern typically shows:
// Sample output showing memory creep $ watch -n 60 'ps -ylC apache2 --sort:rss | awk \'{sum+=$8; ++n} END {print "Tot="sum"("n") Avg="sum/n/1024"MB"}\'' Tot=4235678(24) Avg=172.34MB [2 hours later] Tot=5872345(24) Avg=239.12MB
Before deep diving into PHP, verify your Apache MPM settings match your traffic pattern:
# Check current MPM configuration $ apache2ctl -V | grep -i mpm Server MPM: prefork # Recommended prefork settings for memory-heavy Drupal sitesStartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxRequestWorkers 50 MaxConnectionsPerChild 500
Implement real-time memory tracking in your Drupal environment:
// Add to settings.php for memory logging function drupal_memory_log() { $mem = memory_get_usage(true); file_put_contents('/tmp/drupal_memory.log', date('Y-m-d H:i:s') . " - " . round($mem/1024/1024, 2) . "MB - " . $_SERVER['REQUEST_URI'] . "\n", FILE_APPEND); } register_shutdown_function('drupal_memory_log'); // Alternative: XHProf setup for detailed analysis xhprof_enable(XHPROF_FLAGS_MEMORY | XHPROF_FLAGS_CPU); register_shutdown_function(function() { $data = xhprof_disable(); file_put_contents('/tmp/xhprof.log', serialize($data)); });
Use PHP's built-in functions to track memory allocation:
// Debug function to identify memory hotspots function debug_memory_usage($label = '') { static $last = 0; $current = memory_get_usage(); $change = $current - $last; error_log(sprintf( "%s: %s (%+d)", $label, format_bytes($current), $change )); $last = $current; } // Sample Drupal hook implementation function mymodule_init() { debug_memory_usage('After bootstrap'); // Additional debug points }
For deeper investigation, consider these tools:
- Valgrind Massif:
$ valgrind --tool=massif --pages-as-heap=yes php -r 'apache_request_headers();'
- PHP Memory Profiler:
$ pecl install memprof $ echo "extension=memprof.so" >> /etc/php.ini
- Drupal-specific:
// In settings.php $conf['cache_backends'][] = 'sites/all/modules/memcache/memcache.inc'; $conf['cache_default_class'] = 'MemCacheDrupal';
These Drupal-specific issues frequently cause memory leaks:
// 1. Static variable accumulation function mymodule_get_data() { static $cache = array(); // Without proper cache clearing, this grows indefinitely } // 2. Entity metadata wrappers $node = node_load(123); $wrapper = entity_metadata_wrapper('node', $node); // Always unset wrappers after use // 3. Views execution in loops foreach ($items as $item) { $view = views_get_view('my_view'); $view->execute(); // Should use views_get_view_result() instead }
Implement continuous monitoring with this shell script:
#!/bin/bash while true; do TIMESTAMP=$(date +%Y%m%d-%H%M%S) # Track Apache processes ps -eo pid,rss,command | grep apache2 | grep -v grep > /var/log/apache_memory/$TIMESTAMP.log # Track PHP memory for pid in $(pgrep php); do grep -i rss /proc/$pid/status | awk -v pid=$pid '{print pid,$2}' >> /var/log/php_memory.log done sleep 300 done
Critical PHP.ini adjustments for Drupal:
; Recommended settings for Drupal 6/7 realpath_cache_size = 256K realpath_cache_ttl = 3600 opcache.memory_consumption = 128 opcache.interned_strings_buffer = 16 mysql.connect_timeout = 3 default_socket_timeout = 60