0

Classes, objects, and instantiation — what new actually does in memory

Beginner5 min read·php-07-001
interviewcompare

Concept

When you write new ClassName(), PHP executes several steps internally that are important to understand for memory management and performance. Understanding object instantiation helps you reason about object lifetimes, initialization order, and the cost of object creation.

What new does: (1) Allocates a zend_object struct on the heap — size depends on the number of declared properties. (2) Initializes the object handle (an index into the global object store). (3) Copies default property values from the class's property table into the fresh object. (4) Calls __construct() (if defined). (5) Returns the object handle wrapped in a zval.

Objects are NOT stored in zvals: A zval stores an object_handle integer — an index. The actual zend_object lives in a global object store. When you assign $b = $a (where $a is an object), you get two zvals with the same object_handle — both pointing to the same object. This is why objects appear to behave like pass-by-reference even though PHP is pass-by-value for variables.

Object vs array memory: An empty stdClass object uses ~72 bytes. An object with 5 string properties uses ~200 bytes. Arrays with the same keys use similar amounts. For data-transfer objects (DTOs), typed class properties with PHP 8.0 constructor promotion are often cleaner and more type-safe than arrays at comparable memory cost.

Cloning vs new: clone $obj copies all properties shallowly (like new + copying properties). Nested objects are NOT deep-copied — they still share handles. Use __clone() to perform deep copies.

Code Example

php
<?php
declare(strict_types=1);

class Point
{
    public function __construct(
        public readonly float $x,
        public readonly float $y,
    ) {}
}

// Object instantiation
$p1 = new Point(1.0, 2.0);
$p2 = new Point(3.0, 4.0);

// Objects behave like references — both point to SAME underlying object
$a = $p1;
$a->x;  // reads from same object as $p1 (but x is readonly, can't mutate)

// Creating an object from class name string (dynamic instantiation)
$className = 'Point';
$p3 = new $className(5.0, 6.0); // valid PHP

// Without constructor
$anon = new class {
    public int $value = 0;
};
$anon->value = 42;

// Memory comparison
$before = memory_get_usage();
for ($i = 0; $i < 10_000; $i++) {
    $obj = new Point($i, $i);
}
echo "10k objects: " . (memory_get_usage() - $before) . " bytes\n"; // ~1.6MB

// stdClass for dynamic object creation
$data = new stdClass();
$data->name = 'Alice';
$data->age  = 30;

// json_decode returns stdClass by default
$json = '{"name":"Bob","age":25}';
$obj  = json_decode($json);       // stdClass
$arr  = json_decode($json, true); // associative array

// Checking instantiation
$p = new Point(1.0, 2.0);
var_dump($p instanceof Point);    // true
var_dump(is_object($p));          // true
var_dump(get_class($p));          // "Point"