Voting

: nine plus zero?
(Example: nine)

The Note You're Voting On

marcus at marcusball dot me
4 years ago
When dealing with a very large quantity of objects, it is worth noting that using `get_object_vars()` may drastically increase memory usage.

If instantiated objects only use predefined properties from a class then PHP can use a single hashtable for the class properties, and small memory-efficient arrays for the object properties:

If a class is defined with three properties ($foo, $bar, and $baz), "PHP no longer has to store the data in a hashtable, but instead can say that $foo is proprety 0, $bar is proprety 1, $baz is property 2 and then just store the properties in a three-element C array. This means that PHP only needs one hashtable in the class that does the property-name to offset mapping and uses a memory-efficient C-array in the individual objects."

However, if you call `get_object_vars()` on an object like this, then PHP WILL build a hashtable for the individual object. If you have a large quantity of objects, and you call `get_object_vars()` on all of them, then a hashtable will be built for each object, resulting in a lot more memory usage. This can be seen in this bug report: https://bugs.php.net/bug.php?id=79392

The effects of this can be seen in this example:

<?php
class Example {
public
$foo;
public
$bar;
public
$baz;
}

function
printMem($label) {
$usage = memory_get_usage();
echo
sprintf('%s: %d (%.2f MB)', $label, $usage, $usage / 1000000) . PHP_EOL;
}

printMem('start');

$objects = [];
for (
$i = 0; $i < 20000; $i++) {
$obj = new Example;
$obj->foo = bin2hex(random_bytes(5));
$obj->bar = bin2hex(random_bytes(5));
$obj->baz = bin2hex(random_bytes(5));
$objects[] = $obj;
}

printMem('before get_object_vars');

// Clone each object, and get the vars on the clone
foreach ($objects as $obj) {
$c = clone $obj;
$vars = get_object_vars($c);

// Accessing and modifying the original object is fine.
foreach ($vars as $var => $val) {
$obj->{$var} = strrev($val);
}
}

printMem('get_object_vars using clone');

// Get the vars on each object directly
foreach ($objects as $obj) {
$vars = get_object_vars($obj);

// The memory is used even if you do not modify the object.
}

printMem('get_object_vars direct access');
?>

The output of this is:

start: 405704 (0.41 MB)
before get_object_vars: 6512416 (6.51 MB)
get_object_vars using clone: 6033408 (6.03 MB)
get_object_vars direct access: 13553408 (13.55 MB)

In short, if you are using classes to avoid additional memory usage associated with hashtables (like in associative arrays), be aware that `get_object_vars()` will create a hashtable for any object passed to it.

This appears to be present in all versions of PHP; I've tested it on PHP 5, 7, and 8.

Quotes are from Nikic's blog posts on arrays and hashtable memory usage, and Github gist "Why objects (usually) use less memory than arrays in PHP".

<< Back to user notes page

To Top