note 125993 added to function.get-object-vars
| From: | marcus at marcusball dot me | Date: | Thu, 08 Apr 2021 22:54:04 +0000 |
| Subject: | note 125993 added to function.get-object-vars | ||
| Groups: | php.notes | ||
| Request: | Send a blank email to php-notes+get-216264@lists.php.net to get a copy of this message | ||
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".
----
Server IP: 45.112.84.4
Probable Submitter: 45.112.85.242 (proxied: 72.196.121.168)
----
Manual Page -- https://php.net/manual/en/function.get-object-vars.php
Edit -- https://main.php.net/note/edit/125993
Del: integrated -- https://main.php.net/note/delete/125993/integrated
Del: useless -- https://main.php.net/note/delete/125993/useless
Del: bad code -- https://main.php.net/note/delete/125993/bad+code
Del: spam -- https://main.php.net/note/delete/125993/spam
Del: non-english -- https://main.php.net/note/delete/125993/non-english
Del: in docs -- https://main.php.net/note/delete/125993/in+docs
Del: other reasons-- https://main.php.net/note/delete/125993
Reject -- https://main.php.net/note/reject/125993
Search -- https://main.php.net/manage/user-notes.php