錯誤訊息 Allowed memory size of 8388608 bytes exhausted (tried to allocate 1298358 bytes) 表示 PHP 腳本申請記憶體超過 memory_limit 設定值 (預設 8M/128M/256M 視版本與安裝方式而定)。本文分析常見原因並提供三種解決方案。
fetchAll() 百萬筆資料、file_get_contents() 讀取超大檔案、json_decode() 處理巨大 JSON。; 找到 memory_limit 行,調大 (單位: M/G/K) memory_limit = 256M ; 或移除限制 (極不建議生產環境) ; memory_limit = -1
步驟:
php.ini:php --ini (CLI) 或 phpinfo() (Web) 查看 Loaded Configuration File。優點:一次設定、所有腳本生效。缺點:影響全站、需重啟服務、掩蓋記憶體洩漏 bug。
<?php
// 僅對當前腳本生效,建議放在腳本最頂端
ini_set('memory_limit', '512M'); // 或 '256M', '1G', '-1' (無限制)
// 執行大量記憶體操作
$largeData = file_get_contents('huge_file.xml'); // 200MB
$dom = new DOMDocument();
$dom->loadXML($largeData);
// ...
// 好習慣:操作結束後恢復 (可選)
ini_set('memory_limit', '128M');
?>
注意:ini_set 若在 php.ini 中被 disable_functions 禁用則無效。CLI 模式下通常有效。
# 放在專案根目錄或特定子目錄 php_value memory_limit 256M
需 AllowOverride Options 或 AllowOverride All 才生效。
; 放在專案根目錄 memory_limit = 256M
.user.ini 為 PHP 5.3+ 支援的 per-directory 設定檔,預設 user_ini.cache_ttl = 300 秒 (5 分鐘) 重新讀取,無需重啟服務。
| 問題場景 | 優化策略 | 記憶體影響 |
|---|---|---|
| 大量資料查詢 | 使用 PDO::FETCH_LAZY、游標 (PDO::CURSOR_SCROLL)、分批 LIMIT offset, count、yield 產生器逐行處理 | 從 O(N) 降為 O(1) 或 O(batch) |
| 大檔案讀取/轉換 | 串流處理:fopen() + fgets()/fread()、XMLReader (SAX)、JSON 串流解析器 | 避免完整載入記憶體 |
| 圖片處理 | Imagick 串流、縮圖前先讀取尺寸 (getimagesize())、分塊處理、釋放 $im->destroy() | 減少峰值佔用 |
| 大陣列操作 | unset() 不再需要的變數、array_chunk() 分批、避免多重複製 | 釋放記憶體給 GC |
| Composer 自動載入 | 生產環境執行 composer dump-autoload -o 優化 Class Map | 減少檔案系統統計與載入開銷 |
<?php
// 即時監控
echo "Start: " . memory_get_usage(true) . " bytes\n"; // 含配置給 PHP 的總量
echo "Peak: " . memory_get_peak_usage(true) . " bytes\n";
// 關鍵點插桿
function memCheck($label) {
$mem = memory_get_usage(true);
$peak = memory_get_peak_usage(true);
error_log("[$label] Current: " . number_format($mem/1024/1024, 2) . " MB, Peak: " . number_format($peak/1024/1024, 2) . " MB");
}
memCheck('Before query');
$data = $pdo->query("SELECT * FROM huge_table")->fetchAll();
memCheck('After fetchAll');
unset($data);
memCheck('After unset');
ini_set('memory_limit', '256M') 最快。.user.ini (PHP-FPM) 或 .htaccess (mod_php) 免重啟。php.ini + 重啟服務。