How does PHP Garbage Collection (gc_collect_cycles) work and how do WeakMaps prevent memory leaks in long-running daemons?
❓ Interview Prompt: Explain PHP reference counting (zval refcount), circular reference memory leaks, cyclic garbage collection buffer, and PHP 8.0 WeakMap.
PHP uses Reference Counting ('refcount' inside internal C 'zval' structures) as its primary memory management engine. When a variable is assigned, 'refcount' increments; when unset or out of scope, it decrements. When 'refcount' hits zero, memory is freed instantly.
Circular Reference Leaks in Long-Running Daemons:
In standard PHP-FPM web scripts, circular references ('$a->friend = $b; $b->friend = $a;') don't matter because all memory is wiped when the HTTP request ends. But in long-running CLI daemons, WebSocket servers, or Laravel Octane/Swoole workers, circular references whose counts never reach zero will leak memory continuously until the process crashes!
1. Cyclic Garbage Collection ('gc_collect_cycles'):
PHP maintains a root buffer of potential cycles (default 10,000 roots). When the buffer fills, PHP pauses execution and runs a tracing algorithm to identify and free unreachable circular structures. You can force this manually via 'gc_collect_cycles()'.
2. PHP 8.0+ 'WeakMap':
A 'WeakMap' allows using objects as dictionary keys without incrementing their reference count! If all other references to an object are deleted, the object is automatically garbage collected and purged from the 'WeakMap' without causing memory leaks.
Code Implementation Example
<?php
declare(strict_types=1);
class UserSession {
public function __construct(public int $id) {}
public function __destruct() {
echo "[GC Purge] UserSession #{$this->id} memory freed.
";
}
}
// PHP 8.0+ WeakMap allows GC to reclaim memory automatically!
$weakCache = new WeakMap();
function processUserRequest(WeakMap $cache): void {
$session = new UserSession(101);
// Store object as key in WeakMap (does NOT increment refcount!)
$cache[$session] = ['last_active' => time()];
// When $session goes out of scope, __destruct() fires immediately!
}
processUserRequest($weakCache);
echo "Cache size: " . count($weakCache); // Outputs: 0!In long-running PHP workers (Swoole/Octane), use gc_collect_cycles() to break circular leaks and WeakMap to cache objects without retaining memory.