Skip to content

PHP Tutorial PHP Performance and Best Practices

Avoiding Memory Problems

Lesson 342 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

A common PHP error:

Text
PLAIN
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 8192 bytes)

PHP has a memory_limit setting (128 MB is a typical default for web requests). A script that tries to use more is stopped. The best fix is not to raise the limit, but to use less memory.

Where memory goes

PHP arrays are flexible but not small. An array of one million integers takes tens of megabytes, far more than the raw numbers. Loading a big file, a big query result or a big API response into one variable adds up fast.

index.php
PHP
<?php
$before = memory_get_usage();
$numbers = range(1, 200000);
$used = memory_get_usage() - $before;

echo $used > 1_000_000 ? "200,000 integers use more than 1 MB" : "less than 1 MB";
Output
OUTPUT
200,000 integers use more than 1 MB

1. Process in chunks, not all at once

index.php
PHP
<?php
file_put_contents("big.csv", implode("\n", range(1, 5000)));

// Loads the whole file into an array
$lines = file("big.csv");

// Reads one line at a time
$handle = fopen("big.csv", "r");
$sum = 0;

while (($line = fgets($handle)) !== false) {
    $sum += (int) $line;
}

fclose($handle);

echo count($lines) . " lines, sum $sum";
Output
OUTPUT
5000 lines, sum 12502500

The second approach holds one line in memory at a time, whatever the file size.

2. Use generators

A generator produces one item at a time (see Generators).

index.php
PHP
<?php
function readLines(string $file): Generator
{
    $h = fopen($file, "r");

    try {
        while (($line = fgets($h)) !== false) {
            yield (int) $line;
        }
    } finally {
        fclose($h);
    }
}

file_put_contents("big.csv", implode("\n", range(1, 5000)));

$total = 0;
foreach (readLines("big.csv") as $n) {
    $total += $n;
}

echo $total;
Output
OUTPUT
12502500

3. Query in batches

index.php
PHP
<?php
$pdo = require __DIR__ . "/db.php";

$batchSize = 2;
$lastId = 0;
$processed = [];

while (true) {
    $stmt = $pdo->prepare("SELECT id, name FROM products WHERE id > :last ORDER BY id LIMIT :limit");
    $stmt->bindValue(":last", $lastId, PDO::PARAM_INT);
    $stmt->bindValue(":limit", $batchSize, PDO::PARAM_INT);
    $stmt->execute();

    $rows = $stmt->fetchAll();

    if ($rows === []) {
        break;
    }

    foreach ($rows as $row) {
        $processed[] = $row["name"];
        $lastId = $row["id"];
    }
}

echo implode(", ", $processed);
Output
OUTPUT
Notebook, Pen, Bag, Marker, Diary

Only two rows are in memory at once. For very large tables, MySQL can also stream rows with an unbuffered query (PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false).

4. Select only what you need

Fewer columns and fewer rows mean fewer bytes. Do not load big text or binary columns unless you need them.

5. Free what you no longer need

index.php
PHP
<?php
$big = range(1, 100000);
$count = count($big);

unset($big);   // the memory can be reused

echo $count;
Output
OUTPUT
100000

PHP frees memory when a variable goes out of scope (at the end of a function), so most code does not need unset(). In long loops that build large temporary values, it can help.

6. Avoid unneeded copies

PHP copies arrays only when you change a copy (copy-on-write). Passing a big array to a function does not copy it, unless the function modifies it. Do not worry about reading.

7. Raise the limit only as a last step

index.php
PHP
<?php
ini_set("memory_limit", "512M");   // for one heavy script, e.g. an import

Raising it hides a design problem and can take memory from other requests. For command-line jobs that process large data, use chunking, and set the limit explicitly.

8. Image processing needs care

Resizing a large image decodes it to raw pixels: a 6000 x 4000 photo needs about 96 MB in memory (width x height x 4 bytes). Check an image’s dimensions before you load it.

index.php
PHP
<?php
function imageMemoryBytes(int $width, int $height): int
{
    return $width * $height * 4;
}

echo round(imageMemoryBytes(6000, 4000) / 1048576) . " MB";
Output
OUTPUT
92 MB

9. Find memory leaks in long scripts

Queue workers and import scripts run for a long time. Watch memory_get_usage() over time, clear caches, close handles and avoid storing everything in a growing array.

lightbulb
memory_limit = -1 only on the command line
CLI scripts have no limit by default in many setups. In a web request, a limit protects your server from a single runaway script.