PHP Memory Usage: The cost of a million objects

Even though memory is getting cheaper, our applications are becoming more complicated and we need to write our code more carefully. I’d like to share some surprising tests I’ve written recently. The sample code files are also on github: https://github.com/muratyaman/php-tips.

The tests are very simple: let’s create an array of million items to process:

  • version 1 includes an array of arrays
  • version 2 includes an array of objects using stdClass
  • version 3 includes an array of a custom class using magic methods for attributes
  • version 4 includes an array of a custom class using public attributes (private did not make a difference)

Here is the first version:

 str_shuffle($S),//string
        'b' => rand(0, $L) / rand(1, $L),//float
        'c' => rand(-1000, 1000),//int
    ];
    $arr[] = $row;
}

$t1 = microtime(true);
$mem = memory_get_usage(true);

echo 'Array of ' . number_format($L) . ' arrays.' . PHP_EOL;
echo 'Time taken: ' . number_format($t1 - $t0, 3) . 'ms.' . PHP_EOL;
echo 'Memory usage: ' . number_format($mem / 1024, 1) . 'KB.' . PHP_EOL;
?>

Output of v1 is:

$ php memory_test_v1_arrays.php
Array of 1,000,000 arrays.
Time taken: 3.472ms.
Memory usage: 442,112.0KB.

Likewise, if you follow the versions on github, version 4 has the best results. The code is:

a = $a;
        $this->b = $b;
        $this->c = $c;
    }
}
$arr = [];
$L = 1000000;
$S = 'abcdefghijklmnopqrstuvwxyz';
mt_srand();
for($i = 1; $i <= $L; $i++) {
    $row = new myClass(
        str_shuffle($S),//string
        mt_rand(0, $L) / mt_rand(1, $L),//float
        mt_rand(-1000, 1000)
    );
    $arr[] = $row;
}
$t1 = microtime(true);
$mem = memory_get_usage(true);
echo 'Array of ' . number_format($L) . ' custom objects.' . PHP_EOL;
echo 'Time taken: ' . number_format($t1 - $t0, 3) . 'ms.' . PHP_EOL;
echo 'Memory usage: ' . number_format($mem / 1024, 1) . 'KB.' . PHP_EOL;

Output v4 is:

$ php memory_test_v4_objects_with_myclass_simple.php
Array of 1,000,000 custom objects.
Time taken: 4.137ms.
Memory usage: 295,424.0KB.

What surprised me is that PHP is managing the memory more efficiently when we define simple classes and create objects based on them. It is almost 1/3 of the usage when we have a million objects using a custom class with magic methods for attributes. On the other hand, resetting an array of arrays (and running GC) gives almost all the memory back to the system. Main lesson for me is that magic methods are evil.

NOTE: a useful function to mention is gc_collect_cycles() which enables us to run garbage collection on demand. In my case (Windows 7, PHP 5.6.12) it helped me reduce memory usage i.e. we can run it after setting our array to null (when we finish using it, and we want to continue with some other long process).

Here is a quick snapshot of the chart to compare the results:
php-memory-usage-chart

Author: murat

Leave a Reply