← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · updated 7d
← Back to glossary

Garbage Collection

Compiler PHP 5.3+ Advanced
debt(d7/e3/b5/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). The detection_hints list Blackfire and php-meminfo as tools — these are specialist profilers that require active instrumentation of the running process. Memory growth from circular references in a queue worker is silent in development and only visible when monitoring memory over time in production-like conditions. It doesn't surface as a compiler or linter warning, and default static analysis won't flag missing gc_collect_cycles() calls.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix cites calling gc_collect_cycles() manually in long-running scripts, and common_mistakes point to unsetting large arrays/objects and avoiding circular references in listeners. These fixes are small but require identifying all affected loops and batch-processing points — slightly more than a single one-line patch but confined within one or a few scripts/components.

b5 Burden Structural debt — long-term weight of choosing wrong

Closest to 'persistent productivity tax' (b5). The applies_to scope is cli and queue-worker contexts, not all PHP, so it doesn't reach the entire codebase. However, in any long-running PHP process the concern is pervasive — every loop, every object graph, every event listener registration must be written with GC awareness, imposing an ongoing mental tax on maintainers working in those contexts.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field states directly: developers believe PHP's garbage collector handles all memory automatically, but circular references are only freed by the cycle collector which runs periodically — not immediately on unset. This contradicts the intuitive model that 'when I'm done with an object, memory is freed,' which is how reference counting works for non-circular structures. This mismatch causes real memory exhaustion bugs in production queue workers.

About DEBT scoring →

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
}

Added 15 Mar 2026
Edited 28 Apr 2026
Views 121
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 0 pings W 0 pings T 0 pings F 2 pings S 0 pings S 1 ping M 0 pings T 3 pings W 0 pings T 0 pings F 1 ping S 1 ping S 0 pings M 0 pings T 0 pings W 0 pings T 0 pings F 1 ping S 0 pings S 1 ping M 2 pings T 1 ping W 0 pings T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W
No pings yet today
Google 1
Amazonbot 14 Scrapy 9 Perplexity 7 Google 6 SEMrush 6 Ahrefs 5 Claude 4 Unknown AI 3 Majestic 2 PetalBot 2 Applebot 2 Meta AI 1 Twitter/X 1 Bing 1
crawler 60 crawler_json 2 pre-tracking 1
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
Compiler general A compiler is a program that translates your entire source code into machine-readable instructions before the program runs, catching errors upfront and producing an executable file.

Understanding compilation explains why some errors appear before your code runs, why compiled programs are often faster, and how tools like TypeScript catch bugs at build time rather than in production.

💡 When you see an error before your code runs, that's the compiler helping you—read the line number and fix the syntax first.

Ask Codex about Compiler →
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


✓ schema.org compliant