How to determine the memory footprint (size) of a variable?

Is there a function in PHP (or a PHP extension) to find out how much memory a given variable uses? sizeof just tells me the number of elements/properties.

memory_get_usage helps in that it gives me the memory size used by the whole script. Is there a way to do this for a single variable?

Note that this is on a development machine, so loading extensions or debug tools is feasible.


There's no direct way to get the memory usage of a single variable, but as Gordon suggested, you can use memory_get_usage. That will return the total amount of memory allocated, so you can use a workaround and measure usage before and after to get the usage of a single variable. This is a bit hacky, but it should work.

$start_memory = memory_get_usage();
$foo = "Some variable";
echo memory_get_usage() - $start_memory;

Note that this is in no way a reliable method, you can't be sure that nothing else touches memory while assigning the variable, so this should only be used as an approximation.

You can actually turn that to an function by creating a copy of the variable inside the function and measuring the memory used. Haven't tested this, but in principle, I don't see anything wrong with it:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $tmp = unserialize(serialize($var));
    return memory_get_usage() - $start_memory;
}

You Probably need a Memory Profiler. I have gathered information fro SO but I have copied the some important thing which may help you also.

As you probably know, Xdebug dropped the memory profiling support since the 2.* version. Please search for the "removed functions" string here: http://www.xdebug.org/updates.php

Removed functions

Removed support for Memory profiling as that didn't work properly.

Other Profiler Options

php-memory-profiler

https://github.com/arnaud-lb/php-memory-profiler. This is what I've done on my Ubuntu server to enable it:

sudo apt-get install libjudy-dev libjudydebian1
sudo pecl install memprof
echo "extension=memprof.so" > /etc/php5/mods-available/memprof.ini
sudo php5enmod memprof
service apache2 restart

And then in my code:

<?php
memprof_enable();
// do your stuff
memprof_dump_callgrind(fopen("/tmp/callgrind.out", "w"));

Finally open the callgrind.out file with KCachegrind

Using Google gperftools (recommended!)

First of all install the Google gperftools by downloading the latest package here: https://code.google.com/p/gperftools/

Then as always:

sudo apt-get update
sudo apt-get install libunwind-dev -y
./configure
make
make install

Now in your code:

memprof_enable();

// do your magic

memprof_dump_pprof(fopen("/tmp/profile.heap", "w"));

Then open your terminal and launch:

pprof --web /tmp/profile.heap

pprof will create a new window in your existing browser session with something like shown below:

PHP memory profiling with memprof and gperftools

Xhprof + Xhgui (the best in my opinion to profile both cpu and memory)

With Xhprof and Xhgui you can profile the cpu usage as well or just the memory usage if that's your issue at the moment. It's a very complete solutions, it gives you full control and the logs can be written both on mongo or in the filesystem.

For more details see here.

Blackfire

Blackfire is a PHP profiler by SensioLabs, the Symfony2 guys https://blackfire.io/

If you use puphpet to set up your virtual machine you'll be happy to know it's supported ;-)

Xdebug and tracing memory usage

XDEBUG2 is a extension for PHP. Xdebug allows you to log all function calls, including parameters and return values to a file in different formats.There are three output formats. One is meant as a human readable trace, another one is more suited for computer programs as it is easier to parse, and the last one uses HTML for formatting the trace. You can switch between the two different formats with the setting. An example would be available here

forp

forp simple, non intrusive, production-oriented, PHP profiler. Some of features are:

  • measurement of time and allocated memory for each function

  • CPU usage

  • file and line number of the function call

  • output as Google's Trace Event format

  • caption of functions

  • grouping of functions

  • aliases of functions (useful for anonymous functions)

DBG

DBG is a a full-featured php debugger, an interactive tool that helps you debugging php scripts. It works on a production and/or development WEB server and allows you debug your scripts locally or remotely, from an IDE or console and its features are:

  • Remote and local debugging

  • Explicit and implicit activation

  • Call stack, including function calls, dynamic and static method calls, with their parameters

  • Navigation through the call stack with ability to evaluate variables in corresponding (nested) places

  • Step in/Step out/Step over/Run to cursor functionality

  • Conditional breakpoints

  • Global breakpoints

  • Logging for errors and warnings

  • Multiple simultaneous sessions for parallel debugging

  • Support for GUI and CLI front-ends

  • IPv6 and IPv4 networks supported

  • All data transferred by debugger can be optionally protected with SSL


No, there is not. But you can serialize($var) and check the strlen of the result for an approximation.


In answer to Tatu Ulmanens answer:

It should be noted, that $start_memory itself will take up memory (PHP_INT_SIZE * 8).

So the whole function should become:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $var = unserialize(serialize($var));
    return memory_get_usage() - $start_memory - PHP_INT_SIZE * 8;
}

Sorry to add this as an extra answer, but I can not yet comment on an answer.

Update: The *8 is not definate. It can depend apparently on the php version and possibly on 64/32 bit.


You can't retrospectively calculate the exact footprint of a variable as two variables can share the same allocated space in the memory

Let's try to share memory between two arrays, we see that allocating the second array costs half of the memory of the first one. When we unset the first one, nearly all the memory is still used by the second one.

echo memory_get_usage()."\n"; // <-- 433200
$c=range(1,100);
echo memory_get_usage()."\n"; // <-- 444348 (+11148)
$d=array_slice($c, 1);
echo memory_get_usage()."\n"; // <-- 451040 (+6692)
unset($c);
echo memory_get_usage()."\n"; // <-- 444232 (-6808)
unset($d);
echo memory_get_usage()."\n"; // <-- 433200 (-11032)

So we can't conclude than the second array uses half the memory, as it becomes false when we unset the first one.

For a full view about how the memory is allocated in PHP and for which use, I suggest you to read the following article: How big are PHP arrays (and values) really? (Hint: BIG!)

The Reference Counting Basics in the PHP documentation has also a lot of information about memory use, and references count to shared data segment.

The different solutions exposed here are good for approximations but none can handle the subtle management of PHP memory.

  1. calculating newly allocated space

If you want the newly allocated space after an assignment, then you have to use memory_get_usage() before and after the allocation, as using it with a copy does give you an erroneous view of the reality.

// open output buffer
echo "Result: ";
// call every function once
range(1,1); memory_get_usage();

echo memory_get_usage()."\n";
$c=range(1,100);
echo memory_get_usage()."\n";

Remember that if you want to store the result of the first memory_get_usage(), the variable has to already exist before, and memory_get_usage() has to be called another previous time, and every other function also.

If you want to echo like in the above example, your output buffer has to be already opened to avoid accounting memory needed to open the output buffer.

  1. calculating required space

If you want to rely on a function to calculate the required space to store a copy of a variable, the following code takes care of different optimizations:

<?php
function getMemorySize($value) {
    // existing variable with integer value so that the next line
    // does not add memory consumption when initiating $start variable
    $start=1;
    $start=memory_get_usage();
    // json functions return less bytes consumptions than serialize
    $tmp=json_decode(json_encode($value));
    return memory_get_usage() - $start;
}

// open the output buffer, and calls the function one first time
echo ".\n";
getMemorySize(NULL);

// test inside a function in order to not care about memory used
// by the addition of the variable name to the $_GLOBAL array
function test() {
    // call the function name once 
    range(1,1);

    // we will compare the two values (see comment above about initialization of $start)
    $start=1;
    $start=memory_get_usage();
    $c=range(1,100);
    echo memory_get_usage()-$start."\n";
    echo getMemorySize($c)."\n";
}
test();

// same result, this works fine.
// 11044
// 11044

Note that the size of the variable name matters in the memory allocated.

  1. Check your code!!

A variable has a basic size defined by the inner C structure used in the PHP source code. This size does not fluctuate in the case of numbers. For strings, it would add the length of the string.

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;

If we do not take the initialization of the variable name into account, we already know how much a variable uses (in case of numbers and strings):

44 bytes in the case of numbers

&plus; 24 bytes in the case of strings

&plus; the length of the string (including the final NUL character)

(those numbers can change depending on the PHP version)

You have to round up to a multiple of 4 bytes due to memory alignment. If the variable is in the global space (not inside a function), it will also allocate 64 more bytes.

So if you want to use one of the codes inside this page, you have to check that the result using some simple test cases (strings or numbers) match those data taking into account every one of the indications in this post ($_GLOBAL array, first function call, output buffer, ...)