Garbage Collection
Also Known As
reference counting
cycle collector
GC
memory management
TL;DR
Automatic memory management that reclaims objects no longer reachable by the program — PHP uses reference counting with a cycle collector for circular references.
Explanation
PHP's primary GC is reference counting: each value tracks how many variables point to it; when the count reaches 0, the value is freed immediately. Problem: circular references (A → B → A) never reach 0. PHP's cycle collector (since 5.3) detects and frees circular reference groups. In PHP 8, the GC is more efficient. Long-running processes (queue workers, PHP-FPM long-lived workers) accumulate memory through leaks or slow GC; calling gc_collect_cycles() manually in batch jobs can reduce memory usage.
Diagram
flowchart TD
subgraph Reference Counting
OBJ[Object<br/>refcount=1] -->|assign to 2nd var| RC2[refcount=2]
RC2 -->|unset 1st var| RC1[refcount=1]
RC1 -->|unset all| RC0[refcount=0<br/>free immediately]
end
subgraph Cycle Collector
A[Object A<br/>points to B] -->|circular ref| B[Object B<br/>points to A]
B --> A
RC0B[Neither reaches 0<br/>both stranded]
CYCLE[Cycle Collector<br/>runs periodically] -->|detects cycle| FREE[Frees both]
end
style RC0 fill:#238636,color:#fff
style FREE fill:#238636,color:#fff
Watch Out
⚠ PHP's reference counter frees most objects immediately on unset, but circular references (parent→child→parent) are only reclaimed by the cycle collector — which runs periodically, not instantly. A long-running worker that builds trees without breaking cycles will leak memory steadily.
Common Misconception
✗ PHP's garbage collector handles all memory automatically — circular references are only freed by the cycle collector, which runs periodically; large circular reference structures can accumulate in long-running scripts.
Why It Matters
PHP queue workers and long-running scripts that create circular references or large object graphs can slowly exhaust memory — understanding GC enables writing memory-efficient persistent PHP processes.
Common Mistakes
- Not unsetting large arrays and objects after use in loops — reference count stays > 0 until scope ends.
- Circular references in event listeners — object A registers a listener on object B; both hold references to each other.
- Not calling gc_collect_cycles() in very long batch jobs — cycle collector runs automatically but not immediately.
- PHP generators as memory optimisation — they yield one item at a time, but the generator object itself stays in memory.
Avoid When
- Do not disable gc_disable() in long-running workers without a manual gc_collect_cycles() strategy — circular references accumulate silently until OOM.
- Avoid creating large object graphs with circular parent/child references when a simple array or flat structure would suffice.
When To Use
- Call gc_collect_cycles() explicitly after processing large batches of objects in long-running scripts to reclaim memory from circular references immediately.
- Use WeakReference for cache or observer registrations where the GC should be free to collect the target without the reference preventing it.
Code Examples
💡 Note
The bad example builds a parent/child tree with circular references that the reference counter cannot free — memory grows per iteration. The fix nulls the parent reference before unsetting, breaking the cycle so memory is reclaimed immediately.
✗ Vulnerable
// Circular reference — not immediately freed:
class Node {
public ?Node $parent = null;
public array $children = [];
}
$root = new Node();
$child = new Node();
$root->children[] = $child;
$child->parent = $root; // Circular: root → child → root
unset($root, $child); // Reference count > 0 — not freed immediately
// Freed later by cycle collector — or manually: gc_collect_cycles()
✓ Fixed
// Break circular references explicitly:
function processTree(Node $root): void {
foreach ($root->children as $child) processTree($child);
// Break circular refs before returning:
foreach ($root->children as $child) $child->parent = null;
$root->children = [];
}
// Or use WeakReference for parent pointers — does not prevent GC
class Node {
public ?WeakReference $parent = null; // Weak — GC can collect parent
}
References
Tags
🤝 Adopt this term
£79/year · your link shown here
Added
15 Mar 2026
Edited
28 Apr 2026
Views
56
AI edit
PF Media Bot
Claude Opus 4.5 on refs · 25 Apr 2026
Edits history 1 edit
- refs PF Media Bot Claude Opus 4.5 · 25 Apr 2026
🤖 AI Guestbook educational data only
|
|
Last 30 days
Agents 0
No pings yet today
No pings yesterday
Amazonbot 12
Perplexity 7
Unknown AI 3
Ahrefs 2
Majestic 1
Google 1
SEMrush 1
Also referenced
How they use it
crawler 26
pre-tracking 1
Related categories
⚡
DEV INTEL
Tools & Severity
🟡 Medium
⚙ Fix effort: Medium
⚡ Quick Fix
PHP uses reference counting with a cycle collector — call gc_collect_cycles() manually in long-running CLI scripts processing many objects to reclaim circular reference memory
📦 Applies To
PHP 5.3+
cli
queue-worker
🔗 Prerequisites
🔍 Detection Hints
Memory growing in queue worker over time; circular object references not freed; gc_collect_cycles() never called in long CLI batch
Auto-detectable:
✓ Yes
blackfire
php-meminfo
⚠ Related Problems
🤖 AI Agent
Confidence: Low
False Positives: High
✗ Manual fix
Fix: Medium
Context: File