MeWrite Docs

Fatal error: Allowed memory size exhausted

PHPスクリプトが許可されたメモリ上限を超えた場合に発生するエラー

概要

Fatal error: Allowed memory size of X bytes exhausted は、PHPスクリプトが memory_limit で設定されたメモリ上限を超えた場合に発生するエラーです。

エラーメッセージ

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 65536 bytes) in /app/process.php on line 42
PHP Fatal error: Out of memory (allocated 268435456) (tried to allocate 134217728 bytes)

原因

  1. 大量のデータ処理: 大きな配列やオブジェクトをメモリに保持
  2. 無限ループ: 配列が際限なく増加
  3. ファイル全体の読み込み: 大きなファイルを一度にメモリに読み込む
  4. メモリリーク: 不要なオブジェクトが解放されない
  5. memory_limit が低い: デフォルト設定が不十分

解決策

1. memory_limit を増やす(一時的な対処)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// スクリプト内で変更
ini_set('memory_limit', '256M');
ini_set('memory_limit', '512M');
ini_set('memory_limit', '-1');  // 無制限(非推奨)

// php.ini で変更
// memory_limit = 256M

// .htaccess で変更(Apache)
// php_value memory_limit 256M
1
2
# コマンドラインで変更
php -d memory_limit=256M script.php

2. データを分割して処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// ❌ 大量のデータを一度に取得
$users = $pdo->query("SELECT * FROM users")->fetchAll();
foreach ($users as $user) {
    // 処理
}

// ✅ チャンクで処理
$offset = 0;
$limit = 1000;

while (true) {
    $stmt = $pdo->prepare("SELECT * FROM users LIMIT :limit OFFSET :offset");
    $stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();

    $users = $stmt->fetchAll();
    if (empty($users)) {
        break;
    }

    foreach ($users as $user) {
        // 処理
    }

    $offset += $limit;
    unset($users);  // メモリ解放
}

3. ジェネレーターを使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// ❌ 全データをメモリに保持
function getAllUsers($pdo) {
    return $pdo->query("SELECT * FROM users")->fetchAll();
}

// ✅ ジェネレーターで1件ずつ
function getAllUsers($pdo) {
    $stmt = $pdo->query("SELECT * FROM users");
    while ($row = $stmt->fetch()) {
        yield $row;
    }
}

foreach (getAllUsers($pdo) as $user) {
    // 1件ずつ処理、メモリ効率が良い
}

4. ファイルをストリームで処理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// ❌ ファイル全体をメモリに読み込む
$content = file_get_contents('large_file.csv');
$lines = explode("\n", $content);

// ✅ 1行ずつ読み込む
$handle = fopen('large_file.csv', 'r');
while (($line = fgets($handle)) !== false) {
    // 1行ずつ処理
}
fclose($handle);

// ✅ SplFileObject を使用
$file = new SplFileObject('large_file.csv');
$file->setFlags(SplFileObject::READ_CSV);
foreach ($file as $row) {
    // 1行ずつ処理
}

5. 不要な変数を解放

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function processLargeData() {
    $largeArray = getLargeData();

    // 処理
    $result = process($largeArray);

    // 大きな配列を解放
    unset($largeArray);

    // 追加の処理
    doSomethingElse($result);

    return $result;
}

// ガベージコレクションを強制実行
gc_collect_cycles();

6. 画像処理でのメモリ管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// ❌ 画像リソースが解放されない
function processImages($files) {
    foreach ($files as $file) {
        $image = imagecreatefromjpeg($file);
        // 処理
        imagejpeg($image, $output);
        // imagedestroy を忘れている
    }
}

// ✅ リソースを解放
function processImages($files) {
    foreach ($files as $file) {
        $image = imagecreatefromjpeg($file);
        // 処理
        imagejpeg($image, $output);
        imagedestroy($image);  // メモリ解放
    }
}

7. Laravel/Eloquent でのメモリ管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// ❌ 全レコードをメモリに読み込む
$users = User::all();

// ✅ chunk で分割処理
User::chunk(1000, function ($users) {
    foreach ($users as $user) {
        // 処理
    }
});

// ✅ lazy で遅延読み込み
foreach (User::lazy() as $user) {
    // 1件ずつ処理
}

// ✅ cursor でメモリ効率良く
foreach (User::cursor() as $user) {
    // 1件ずつ処理
}

8. Composer でのメモリ問題

1
2
3
4
5
# Composer のメモリ制限を増やす
COMPOSER_MEMORY_LIMIT=-1 composer install

# または
php -d memory_limit=-1 /usr/local/bin/composer install

デバッグのコツ

メモリ使用量の確認

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 現在のメモリ使用量
echo memory_get_usage() . " bytes\n";

// ピーク時のメモリ使用量
echo memory_get_peak_usage() . " bytes\n";

// 人間が読みやすい形式
function formatBytes($bytes) {
    $units = ['B', 'KB', 'MB', 'GB'];
    $i = 0;
    while ($bytes >= 1024 && $i < count($units) - 1) {
        $bytes /= 1024;
        $i++;
    }
    return round($bytes, 2) . ' ' . $units[$i];
}

echo formatBytes(memory_get_usage()) . "\n";

メモリ制限の確認

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// 現在のメモリ制限
echo ini_get('memory_limit') . "\n";

// バイト数に変換
function parseMemoryLimit($limit) {
    $limit = trim($limit);
    $last = strtolower($limit[strlen($limit) - 1]);
    $limit = (int) $limit;
    switch ($last) {
        case 'g': $limit *= 1024;
        case 'm': $limit *= 1024;
        case 'k': $limit *= 1024;
    }
    return $limit;
}

PHP の他のエラー

最終更新: 2025-12-08