PHP Allowed Memory Size Exhausted: Causes and Fixes

2026-11-03来源 dev.to PHPEnglish

The error looks like this, and it always stops the script on the spot:

PHP Fatal error: Allowed memory size of 33554432 bytes exhausted (tried to allocate 33554440 bytes) in /var/www/app/report.php on line 3 

The quick fix is to raise memory_limit. The better fix is to find out why the script needs that much memory, because in most cases it is holding something it could have processed piece by piece. Here is how to read the message, raise the limit properly, and cut the memory use itself.

Reading the message

  • Allowed memory size of 33554432 bytes is your memory_limit in bytes. Divide by 1,048,576: this one is 32 MB.
  • tried to allocate 33554440 bytes is the single allocation that failed, not the total used. A big number here usually means one large array or string being created or grown. PHP arrays resize by doubling, so a loop that appends to an array fails at a request about the size of the array so far.
  • in ... on line N is where the limit was hit, which is not always where the memory was wasted. The line that fails is just the one that asked for memory when none was left. The real cause is often earlier.

A tiny example that fails with a 32 MB limit:

$a = []; for ($i = 0; $i < 10000000; $i++) { $a[] = $i; } 
php -d memory_limit=32M a.php 

That prints the exact error above, including the doubled allocation.

Step 1: Find the limit that actually applies

The CLI, PHP-FPM and Apache can each use a different php.ini, so the value you see in a terminal may not be the one your web request runs with.

php --ini # which php.ini the CLI loads php -r 'echo ini_get("memory_limit");' php-fpm8.3 -i | grep memory_limit # FPM's value (binary name varies by distro) 

On the machine I tested, the CLI php.ini set memory_limit to -1 (unlimited), while PHP's built-in default, checked with php -n, is 128M. That is a common reason for "it works from the command line but fails on the web": the CLI has no limit and the web server does.

Step 2: Raise it, in the right place

Where How Scope
php.ini memory_limit = 256M Everything using that ini file. Restart FPM or Apache after changing it.
Command line php -d memory_limit=512M script.php That one run
In the script ini_set('memory_limit', '256M'); The current request or process
FPM pool file php_admin_value[memory_limit] = 256M One pool

ini_set() worked in my test: a script started with an 8 MB limit raised itself to 64 MB and then allocated a 30 MB string. Be aware that a value set with php_admin_value in the FPM pool configuration cannot be changed afterwards with ini_set(), according to the PHP manual, so if your ini_set seems to be ignored on the web, look at the pool file. I did not test FPM here.

Composer runs out of memory often and has its own switch:

COMPOSER_MEMORY_LIMIT=-1 composer update # or php -d memory_limit=-1 $(which composer) update 

Avoid memory_limit = -1 for web requests. The limit is what stops one runaway request from taking down the whole server. Use a number, and size it from measurements.

Step 3: Use less memory

Raising the limit hides the problem until the data grows again. These are the usual patterns that use far more memory than needed. The numbers below are from PHP 8.3.6 on a 70 MB text file with 1.5 million lines:

Approach Peak memory
$lines = file('big.txt'); 216 MB (fails under a 64 MB limit)
while (($line = fgets($h)) !== false) 2 MB
Generator that yields one line at a time 2 MB

Note that the file is 70 MB but file() needs triple that, because every line becomes a separate PHP string with its own overhead. A generator keeps the code readable while holding one item at a time:

function lines(string $path): Generator { $h = fopen($path, 'r'); try { while (($line = fgets($h)) !== false) { yield $line; } } finally { fclose($h); } } foreach (lines('big.txt') as $line) { // process one line } 

The same shape applies to data that is generated rather than read. With 2 million small values, building an array with iterator_to_array() peaked at 126 MB, while looping over the generator directly peaked at 2 MB. If you only need to loop once, do not collect first.

Database results (Laravel)

The most common source in Laravel apps is Model::all() or ->get() on a large table, which hydrates every row into a model object at once. Process in batches instead:

// Fixed-size batches, safe when you update rows while looping User::query()->chunkById(1000, function ($users) { foreach ($users as $user) { // ... } }); // Same idea, one model at a time through a lazy collection foreach (User::query()->lazyById(1000) as $user) { // ... } 

I did not run these against a database for this article. They are documented Eloquent methods. Prefer chunkById or lazyById over chunk when the loop changes the column you filter on, because chunk uses offsets and can skip rows. If you only need a few columns, select() them. Exporting a whole table to CSV belongs in a queued job that streams, not in a request.

Step 4: Find what is using the memory

If it is not obvious, log the numbers when the limit is hit. A shutdown function still runs after this fatal error, so it can report where things stood:

register_shutdown_function(function () { $e = error_get_last(); if ($e && str_contains($e['message'], 'Allowed memory size')) { error_log(sprintf( 'OOM peak=%.1f MB at %s:%d', memory_get_peak_usage(true) / 1048576, basename($e['file']), $e['line'] )); } }); 

In my test this printed peak=16.0 MB and the failing line for a script limited to 16 MB. For finding the cause, call memory_get_usage() before and after suspicious steps and see which one jumps. A profiler such as Xdebug or Blackfire gives a full picture when that is not enough, though I did not use one for this article.

Things worth checking first:

  • Queries without a limit, and results collected into an array "to process later".
  • file_get_contents() or file() on user uploads or exports.
  • Image processing: the decoded bitmap in memory is far larger than the file on disk, since its size follows the pixel dimensions, not the file size.
  • An infinite loop or runaway recursion that keeps adding data. If the usage grows forever whatever limit you set, this is it.
  • Long-running processes such as queue workers, where leaks accumulate across jobs. Laravel's queue:work has a --memory option (128 MB by default) that makes the worker exit and restart when it grows too large.

Sources

Originally published on DEV Talk.

原文:https://www.a8q9.cn/en/article/php-allowed-memory-size-exhausted-causes-and-fixes-5aac05.html

相关文章