PHP — Allowed memory size exhausted
Diagnose PHP memory exhaustion by locating the peak allocation, fixing unbounded data loading, and raising memory_limit only with evidence.
Symptoms
- Fatal error: Allowed memory size of 134217728 bytes exhausted
- The error occurs on one page, import, or report while other pages work
- Memory grows with the number of rows or uploaded file size
- The process is killed by the container limit before PHP reports the error
Possible causes
- A query loads an entire table into an array instead of iterating
- A file is read fully into memory rather than streamed
- A recursive structure or unbounded cache grows without release
- The memory_limit is lower than the legitimate working set
- The container memory limit is lower than PHP's memory_limit
Diagnosis steps
Confirm the effective limit and the failing script
php -i | grep memory_limit && php -r 'echo ini_get("memory_limit"), PHP_EOL;'Interpretation
CLI and FPM can have different limits. The error identifies the script and line that exhausted the limit.
Measure peak usage around the suspected operation
php -r 'echo memory_get_peak_usage(true), PHP_EOL;'Interpretation
Instrument the import or query with peak usage before and after; a large jump points to data loading.
Inspect the data access pattern
grep -n 'fetchAll\|file_get_contents\|json_decode\|->find(' path/to/file.phpInterpretation
fetchAll, full-file reads, and recursive hydration are common causes of unbounded memory.
Root cause
The PHP process exceeded memory_limit or the container limit while holding more data than the configured allowance. The root cause is usually an unbounded data structure or an undersized limit.
Fix
Stream large inputs, paginate or chunk database results, release references, and cap caches. Raise memory_limit only when the working set is legitimately large and the container has headroom; keep the container limit above PHP's limit.
Risk note
Raising memory_limit without fixing an unbounded query can move the failure to the container OOM killer, which is harder to diagnose and may kill unrelated processes.
Verify the fix
Run the failing script with production-like data and record peak memory. Confirm it stays below the limit with headroom, and run a load test on the endpoint. Check the container memory limit and PHP limit together.
Find the allocation
A memory exhaustion error names the script and line, but the allocation often happened earlier. Instrument peak usage around the suspected import, query, or response construction. A jump from a few megabytes to hundreds points to a collection being loaded at once.
$before = memory_get_peak_usage(true);
$rows = $pdo->query('SELECT * FROM events')->fetchAll();
$after = memory_get_peak_usage(true);
error_log("peak grew by " . ($after - $before));
Stream and chunk
Replace fetchAll with an unbuffered or chunked iteration. Stream file uploads and downloads instead of file_get_contents. Use generators for large transformations. Release large references when they are no longer needed. A cache with no size limit is a memory leak with a friendly name.
Size the limits together
PHP’s memory_limit and the container limit must be compatible: keep the container limit above PHP’s limit with headroom for the runtime and extensions. Raising PHP’s limit inside a container that is already at its ceiling just changes which layer kills the process.
Verify with production-sized data
Run the exact failing operation with representative row counts and file sizes. Confirm peak memory stays below the limit with margin, and test under concurrent requests. A fix verified only with a ten-row fixture is not verified.