{
    "slug": "memory_pressure_detection",
    "term": "Memory Pressure Detection",
    "category": "performance",
    "difficulty": "intermediate",
    "short": "Proactively identifying when a PHP process approaches its memory limit so corrective action can be taken before a fatal error.",
    "long": "Memory pressure occurs when a PHP process consumes a significant fraction of its allowed memory (set by memory_limit in php.ini). Without detection, the first symptom is usually a fatal 'Allowed memory size exhausted' error that kills the process mid-request or mid-job. Memory pressure detection involves periodically calling memory_get_usage() and comparing it against memory_get_peak_usage() or the configured limit. In long-running processes like queue workers, CLI importers, and event loops, memory can creep upward due to accumulating caches, uncollected cycles, or ORM identity maps. Detection strategies include polling memory usage inside batch loops, setting thresholds (e.g. 80% of memory_limit) and gracefully exiting or flushing caches when exceeded, and using gc_collect_cycles() to reclaim circular references. Frameworks like Laravel provide worker options (--memory=128) that check memory after each job. For production visibility, APM tools (Datadog, New Relic, Tideways) track per-process memory and alert on thresholds. The key principle is to fail gracefully or self-heal rather than crash. A queue worker that detects pressure can finish its current job, exit cleanly, and let its supervisor restart it with a fresh memory slate. This pattern prevents data corruption from mid-operation crashes and keeps throughput stable. Combine detection with investigation: if memory rises monotonically across requests, you likely have a memory leak that needs profiling with tools like Xdebug or php-meminfo rather than just periodic restarts.",
    "aliases": [
        "memory threshold monitoring",
        "OOM prevention",
        "memory limit detection",
        "memory usage monitoring"
    ],
    "tags": [
        "performance",
        "memory",
        "php",
        "queue-worker",
        "monitoring",
        "reliability"
    ],
    "misconception": "Increasing memory_limit is sufficient to solve memory pressure issues. Raising the limit only delays the crash - if usage grows without bound (a leak or unbounded dataset), the process will eventually exhaust any limit. Detection and graceful handling address the symptom while profiling addresses the root cause.",
    "why_it_matters": "Undetected memory pressure leads to fatal errors that kill requests mid-response or abort long-running jobs, causing data loss, broken user experiences, and cascading failures in worker pools.",
    "common_mistakes": [
        "Never checking memory_get_usage() in long-running CLI scripts or queue workers until a fatal OOM crash occurs.",
        "Using memory_get_usage() without the real_usage parameter, which underreports actual memory allocated from the OS.",
        "Setting the detection threshold too high (e.g. 99%) leaving no headroom for the current operation to finish cleanly.",
        "Restarting workers on every single job instead of using memory thresholds, which wastes startup cost and hides the real leak.",
        "Forgetting to call gc_collect_cycles() before measuring, leading to inflated readings from uncollected circular references."
    ],
    "when_to_use": [
        "Long-running queue workers or daemon processes that handle many jobs per process lifetime.",
        "CLI batch importers processing large or unbounded datasets.",
        "Swoole/RoadRunner workers that persist across thousands of requests without restarting.",
        "Any process where a fatal OOM error would cause data corruption or lost work."
    ],
    "avoid_when": [
        "Short-lived PHP-FPM requests with well-bounded data - the process dies after each request anyway.",
        "You have already identified and fixed the root-cause memory leak - detection without a leak is unnecessary overhead.",
        "Memory limit is set to -1 (unlimited) in a trusted environment and monitoring is handled externally by container orchestration (e.g. Kubernetes OOMKilled)."
    ],
    "related": [
        "memory_leak",
        "batch_processing",
        "queue_worker_tuning",
        "profiling",
        "worker_pool_php"
    ],
    "prerequisites": [
        "memory_leak"
    ],
    "refs": [
        "https://www.php.net/manual/en/function.memory-get-usage.php",
        "https://www.php.net/manual/en/function.memory-get-peak-usage.php",
        "https://laravel.com/docs/queues#supervisor-configuration",
        "https://www.php.net/manual/en/function.gc-collect-cycles.php"
    ],
    "bad_code": "// Queue worker with no memory awareness - crashes mid-job\nwhile (true) {\n    $job = $queue->pop();\n    if ($job) {\n        $job->handle(); // Memory grows each iteration\n        // No check - eventually: Fatal error: Allowed memory size exhausted\n    }\n    usleep(100000);\n}\n\n// Batch import with no pressure detection\nforeach ($millionRows as $row) {\n    $entities[] = Entity::fromRow($row); // Array grows without bound\n}\n$em->flush();",
    "good_code": "// Parse memory_limit into bytes\nfunction getMemoryLimitBytes(): int {\n    $limit = ini_get('memory_limit');\n    if ($limit === '-1') return PHP_INT_MAX;\n    $unit = strtolower(substr($limit, -1));\n    $bytes = (int) $limit;\n    return match ($unit) {\n        'g' => $bytes * 1024 * 1024 * 1024,\n        'm' => $bytes * 1024 * 1024,\n        'k' => $bytes * 1024,\n        default => $bytes,\n    };\n}\n\n$threshold = (int) (getMemoryLimitBytes() * 0.80);\n\n// Queue worker with graceful exit on memory pressure\nwhile (true) {\n    $job = $queue->pop();\n    if ($job) {\n        $job->handle();\n    }\n\n    if (memory_get_usage(true) >= $threshold) {\n        echo \"Memory threshold reached, exiting for supervisor restart.\\n\";\n        exit(0); // Clean exit - supervisor (systemd/supervisord) restarts\n    }\n    usleep(100000);\n}\n\n// Batch import with chunked processing and pressure check\nforeach (array_chunk($millionRows, 500) as $chunk) {\n    foreach ($chunk as $row) {\n        $em->persist(Entity::fromRow($row));\n    }\n    $em->flush();\n    $em->clear(); // Detach entities, free memory\n\n    gc_collect_cycles();\n    if (memory_get_usage(true) >= $threshold) {\n        throw new MemoryPressureException('Batch aborted: memory pressure');\n    }\n}",
    "quick_fix": "Add a memory check inside your batch or worker loop: if (memory_get_usage(true) > 0.8 * $limitBytes) { break; } and let the supervisor restart the process.",
    "severity": "high",
    "effort": "low",
    "created": "2026-05-06",
    "updated": "2026-05-06",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/memory_pressure_detection",
        "html_url": "https://codeclaritylab.com/glossary/memory_pressure_detection",
        "json_url": "https://codeclaritylab.com/glossary/memory_pressure_detection.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[Memory Pressure Detection](https://codeclaritylab.com/glossary/memory_pressure_detection) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/memory_pressure_detection"
            }
        }
    }
}