Debugging memory usage in PHP apps

As your app gets increasingly more complex, you might run into Memory exhausted errors, and even though you can always increase the allowed memory usage — either by tweaking php.ini or locally with ini_set() — it should be a better option to find out what’s using so much memory in the first place.

First, you should install Xdebug, a debugger and profiler tool for PHP that’s quite popular and it’s probably already available on your OS repositories. In Ubuntu, you can install it with sudo apt-get install php5-xdebug

Check your php_info() to check if the extension is being loaded. If you’re using PHP-FPM, remember that you should restart the service with sudo service php5-fpm restart. Since loading this extension can affect performance, you should only enable it on your development server.

Then, we’ll need to enable function traces. Edit /etc/php5/mods-available/xdebug.ini and add the following:

xdebug.trace_enable_trigger = 1
xdebug.trace_format = 1

The first line will enable function traces but only when the current URL uses a special parameter: http://dev.lo/index.php?XDEBUG_TRACE=1 (check the extension docs to see how you can also enable the trigger with a POST parameter or a cookie).

The second line will set the trace format to a Tab-Separated-Values format, which we’ll use as input on a script that can summarize the generated data.

When you hit the URL you want to debug, the function trace report will be generated on your tmp files, for instance: /tmp/trace.2931297.xt

Now that you have your trace data, get the tracefile analyser from Derick Rethans’ (the php5-xdebug developer) blog or directly from his GitHub repo.

Call the script from the command line. You can run php tracefile-analyser.php with no arguments to see the available options.

For instance, running php tracefile-analyzer.php trace.2987318.xt memory-own 20 will show the 20 most costly calls sorted by memory-own

This will get you a solid starting point to begin analyzing and improving your code.

The output will be something like this:

Inclusive        Own
function                                        #calls  time     memory  time     memory
----------------------------------------------------------------------------------------
{main}                                               1  6.2529 20038368  0.0004      328
require                                            124  6.2525 20037984  0.0085      744
require_once                                        24  6.0223 18954720  0.0295      272
call_user_func_array                              1068  3.6975  2314968  0.0170    91640
get_object_vars                                   1065  0.0163  1964720  0.0163  1964720
do_action                                          188  3.3480  1904872  0.0049   -19624
preg_replace                                      2943  0.0507  1778248  0.0507  1778248
array_keys                                         573  0.0081  1749608  0.0081  1749608
str_replace                                       2065  0.0288  1306552  0.0288  1306552
debug_backtrace                                     43  0.0024  1219280  0.0024  1219280
WP_Query->query                                      3  0.2543  1152352  0.0001    -8016
WP_Query->get_posts                                  3  0.2539  1149464  0.0017   -25720
wp_cache_get                                      2296  0.1765  1102160  0.0469      168
WP_Object_Cache->get                              2296  0.1296  1101992  0.0694   991784
wp                                                   1  0.2217  1082456  0.0000       88
WP->main                                             1  0.2216  1082368  0.0001     1904
wpdb->get_results                                   14  0.1221  1063720  0.0034    58024
WP->query_posts                                      1  0.2171  1040112  0.0000      216
update_post_caches                                   5  0.1718  1038048  0.0003   -15760
_prime_post_caches                                   2  0.1521  1006248  0.0001   -84472