首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 記憶體耗盡錯誤:原因分析與三種快速解決方案

     編輯:PHP 相關知識

錯誤訊息 Allowed memory size of 8388608 bytes exhausted (tried to allocate 1298358 bytes) 表示 PHP 腳本申請記憶體超過 memory_limit 設定值 (預設 8M/128M/256M 視版本與安裝方式而定)。本文分析常見原因並提供三種解決方案。

常見觸發原因

  1. 大量資料一次性載入:fetchAll() 百萬筆資料、file_get_contents() 讀取超大檔案、json_decode() 處理巨大 JSON。
  2. 影像處理:GD/Imagick 處理高解析度圖片解碼後佔用大量記憶體 (寬×高×4 bytes/像素)。
  3. 遞迴/迴圈記憶體洩漏:未釋放資源、陣列持續增長、閉包循環參考。
  4. 框架/套件開銷:Laravel/Symfony 完整啟動即佔用 10-30MB,加上請求處理易超過低限制。

方案一:修改 php.ini (永久、全域生效)

; 找到 memory_limit 行,調大 (單位: M/G/K)
memory_limit = 256M
; 或移除限制 (極不建議生產環境)
; memory_limit = -1

步驟:

  1. 定位正確 php.ini:php --ini (CLI) 或 phpinfo() (Web) 查看 Loaded Configuration File。
  2. 修改並儲存。
  3. 重啟 Web 伺服器 (Apache/Nginx+PHP-FPM) 或重啟 PHP-FPM 服務。

優點:一次設定、所有腳本生效。缺點:影響全站、需重啟服務、掩蓋記憶體洩漏 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 模式下通常有效。

方案三:.htaccess / .user.ini (目錄層級、無需重啟)

Apache + mod_php (.htaccess)

# 放在專案根目錄或特定子目錄
php_value memory_limit 256M

需 AllowOverride Options 或 AllowOverride All 才生效。

PHP-FPM / CGI / FastCGI (.user.ini)

; 放在專案根目錄
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 + 重啟服務。
  • 長期健康:務必優化程式碼,避免記憶體洩漏與一次性載入巨大資料。調大限制只是緩兵之計。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved