• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 9+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Overcoming Performance Bottlenecks: A Technical Audit of PHP-FPM memory consumption per child process on PHP

Overcoming Performance Bottlenecks: A Technical Audit of PHP-FPM memory consumption per child process on PHP

Diagnosing PHP-FPM Child Process Memory Footprint

A common performance bottleneck in PHP applications, especially those experiencing high traffic or complex operations, is the memory consumption of individual PHP-FPM child processes. Uncontrolled memory growth can lead to increased swap usage, slower request processing, and ultimately, OOM (Out Of Memory) killer interventions. This audit focuses on precisely measuring and understanding this per-child memory footprint.

Establishing a Baseline: Initial Memory Usage

Before we can optimize, we need to know where we stand. We’ll start by observing the memory usage of a newly spawned PHP-FPM worker. This requires access to the server’s command line and knowledge of your PHP-FPM pool configuration.

First, identify your PHP-FPM pool configuration file. This is typically located in directories like /etc/php/[version]/fpm/pool.d/www.conf or similar. Within this file, locate the pm.max_children setting. This dictates the maximum number of child processes your pool will spawn.

Next, we need to configure PHP-FPM to run in a static process manager mode temporarily for accurate measurement. This prevents dynamic scaling from masking the true per-process memory usage. Edit your pool configuration file and set:

pm = static
pm.max_children = 5

Restart PHP-FPM to apply these changes:

sudo systemctl restart php[version]-fpm

Now, we can inspect the memory usage of the running PHP-FPM processes. Use the ps command with specific formatting options to get the Resident Set Size (RSS), which represents the non-swapped physical memory a process has used.

Execute the following command. Replace [php-fpm-user] with the user your PHP-FPM pool runs as (often found in the user directive of your pool config).

ps -U [php-fpm-user] -o pid,rss,cmd | grep "php-fpm: pool www" | grep -v grep

The output will show PIDs, RSS in kilobytes, and the command. Observe the RSS for several of the php-fpm: pool www entries. This is your baseline for a “cold” child process. Note this value.

Simulating Load and Measuring Memory Growth

A cold process is rarely representative of real-world usage. We need to simulate requests that exercise various parts of your application to see how memory scales. For this, we’ll use a simple PHP script that performs a common, memory-intensive task, and then use a load testing tool.

Create a test script, for example, memory_test.php, in your web root:

<?php
// memory_test.php

// Simulate loading a large dataset or performing complex computations
$data = [];
$iterations = 50000; // Adjust this based on your application's typical data size

for ($i = 0; $i < $iterations; $i++) {
    $data[] = str_repeat('X', 100); // Allocate 100 bytes per item
}

// Optionally, perform some processing
// unset($data); // Uncomment to see memory release behavior

echo "Memory test script executed. Data size: " . count($data) . "\n";
?>

Now, we’ll use ab (ApacheBench) to hit this script repeatedly. Ensure ab is installed on your system (e.g., sudo apt-get install apache2-utils).

Run the load test. We’ll send 100 requests concurrently to the test script. After the test, immediately re-run the ps command from the previous step.

ab -n 100 -c 100 http://your-domain.com/memory_test.php
ps -U [php-fpm-user] -o pid,rss,cmd | grep "php-fpm: pool www" | grep -v grep

Compare the RSS values of the PHP-FPM processes *after* the load test to your baseline. You should observe an increase. The difference indicates the memory consumed by your application code and its dependencies during request processing. If this increase is substantial and consistently grows with more requests, you’ve identified a memory leak or inefficient memory usage pattern.

Advanced Analysis: Tracking Memory Over Time

For more persistent issues, a single snapshot isn’t enough. We need to monitor memory usage over a longer period, especially under sustained load. This can be achieved by scripting the ps command and logging the output.

Create a shell script (e.g., monitor_fpm_memory.sh) to periodically capture and log memory data:

#!/bin/bash

LOG_FILE="/var/log/php-fpm_memory.log"
INTERVAL_SECONDS=10
POOL_USER="[php-fpm-user]" # Replace with your FPM user
POOL_NAME="www" # Replace if your pool has a different name

echo "Starting PHP-FPM memory monitoring..." >> $LOG_FILE
echo "Timestamp,PID,RSS_KB,CMD" >> $LOG_FILE

while true; do
    TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S")
    # Get PIDs and RSS for the specific FPM pool
    # Using awk to parse and format output, handling potential spaces in CMD
    ps -U $POOL_USER -o pid,rss,cmd | awk -v ts="$TIMESTAMP" -v pool="$POOL_NAME" '
    /php-fpm: pool / {
        # Check if the command line actually contains the pool name
        if ($NF ~ "pool " pool) {
            pid = $1
            rss = $2
            # Reconstruct command, handling spaces
            cmd = ""
            for (i = 3; i <= NF; i++) {
                cmd = cmd $i " "
            }
            # Trim trailing space from cmd
            sub(/ $/, "", cmd)
            print ts "," pid "," rss "," cmd
        }
    }' >> $LOG_FILE
    sleep $INTERVAL_SECONDS
done

Make the script executable and run it in the background:

chmod +x monitor_fpm_memory.sh
nohup ./monitor_fpm_memory.sh &

Let this script run while your application is under its typical load for an extended period (hours or even days). Then, stop the script (find its PID with pgrep -f monitor_fpm_memory.sh and use kill).

Analyze the /var/log/php-fpm_memory.log file. Look for:

  • Consistently increasing RSS for individual PIDs over time. This strongly indicates a memory leak within the application code or a loaded extension.
  • Sudden spikes in RSS that don’t return to a baseline.
  • The average RSS of child processes exceeding acceptable thresholds (e.g., > 128MB, > 256MB depending on your server’s RAM).

You can use tools like awk or import the CSV into a spreadsheet or database for easier analysis and visualization.

Identifying the Source: Code-Level Profiling

Once a memory leak or excessive consumption is confirmed, the next step is to pinpoint the exact code responsible. PHP’s built-in memory profiling tools, combined with external profilers, are invaluable here.

1. Using memory_get_usage() and memory_get_peak_usage():

Sprinkle these functions throughout your code, particularly in areas suspected of high memory usage or leaks. Log their output.

<?php
// In a suspected function or loop
error_log("Before operation: Memory usage: " . memory_get_usage(true) . " bytes");
error_log("Before operation: Peak memory usage: " . memory_get_peak_usage(true) . " bytes");

// ... perform memory-intensive operation ...

error_log("After operation: Memory usage: " . memory_get_usage(true) . " bytes");
error_log("After operation: Peak memory usage: " . memory_get_peak_usage(true) . " bytes");
?>

The true parameter provides real size, including overhead. Analyze the logs to see where memory usage jumps significantly and doesn’t decrease.

Leveraging Xdebug and Profilers

For a more systematic approach, use a profiler like Xdebug. Configure Xdebug to collect memory usage information.

In your php.ini (or a dedicated xdebug.ini file):

xdebug.mode = profile
xdebug.output_mode = json
xdebug.profiler_output_dir = "/tmp/xdebug_profiles"
xdebug.profiler_enable_trigger = 1
xdebug.collect_memory_garbage_statistics = 1

Restart PHP-FPM. Then, trigger profiling for a specific request. You can do this by setting a cookie (e.g., XDEBUG_PROFILE=1) or a GET/POST parameter.

After making the profiled request, a JSON file will appear in /tmp/xdebug_profiles. Use tools like KCachegrind (via QCacheGrind or Webgrind) to analyze these profiles. Look for functions that consume the most memory and have high “Self” memory usage, indicating they are allocating memory directly.

PHP-FPM Configuration Tuning for Memory Management

Once the root cause is identified and addressed in code, you can fine-tune PHP-FPM settings. Remember, these are often workarounds, not fixes for underlying code issues.

1. pm.max_requests: The “Process Restart” Strategy

This is the most common mechanism to combat memory leaks in PHP-FPM. Setting pm.max_requests to a reasonable value (e.g., 500-1000) will cause PHP-FPM to kill and respawn child processes after they have handled a certain number of requests. This effectively clears memory leaks.

pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 2
pm.max_spare_servers = 10
pm.max_requests = 500

Caveats:

  • Too low a value leads to frequent process restarts, impacting performance due to overhead.
  • Too high a value allows leaks to accumulate significantly before a restart.
  • This does not fix the leak; it merely mitigates its impact.

2. Adjusting pm.max_children and related settings:

If your application legitimately requires more memory per process, you might need to increase pm.max_children. However, this must be done cautiously, ensuring your server has sufficient RAM. Monitor overall server memory usage (free -h, top) and swap usage. If swap usage increases significantly, you’re likely over-allocating.

The interplay between pm.max_children, pm.start_servers, pm.min_spare_servers, and pm.max_spare_servers is crucial for balancing performance and resource utilization. Start with conservative values and tune based on observed load and memory usage.

Conclusion

Systematically auditing PHP-FPM memory consumption involves establishing baselines, simulating realistic load, monitoring over time, and employing code-level profiling. While pm.max_requests offers a practical mitigation for memory leaks, the ultimate solution lies in identifying and fixing the root cause within the application code. Continuous monitoring and profiling are key to maintaining a performant and stable PHP environment.

Primary Sidebar

A little about the Author

Having 9+ Years of Experience in Software Development.
Expertised in Php Development, WordPress Custom Theme Development (From scratch using underscores or Genesis Framework or using any blank theme or Premium Theme), Custom Plugin Development. Hands on Experience on 3rd Party Php Extension like Chilkat, nSoftware.

Recent Posts

  • Disaster Recovery 101: Architecting Auto-Failovers for Redis and PHP Deployments on OVH
  • How We Audited a High-Traffic WooCommerce Enterprise Stack on Google Cloud and Mitigated Race conditions during high-concurrency payment processing
  • Disaster Recovery 101: Architecting Auto-Failovers for Elasticsearch and Magento 2 Deployments on DigitalOcean
  • An Auditor’s Checklist for Securing WordPress Backends on OVH
  • Step-by-Step: Diagnosing Perl script high CPU throttling due to unoptimized regular expressions on AWS Servers

Copyright © 2026 · Vinay Vengala