目錄
PHP「Not enough Memory」實戰排錯筆記
1. 背景
2. 快速定位
3. 為什么 5 MB 的圖片能耗盡 128 MB?
3.1 粗略估算公式(GD)
4. 實際峰值監控
5. 解決過程
6. 最佳實踐與防御措施
7. 總結
PHP「Not enough Memory」實戰排錯筆記
——一次 5 MB 圖片上傳導致的內存溢出
1. 背景
-
項目框架:Nginx + PHP-FPM 8.2
-
文件管理器:Responsive Filemanager
-
現象:上傳一張 5 MB 的 JPEG 原圖時,瀏覽器白屏,
error_log
報:Not enough Memory (@/home/www/wwwroot/hnusri.cn/http/manager/plugins/ResponsiveFilemanager/filemanager/upload.php#241)
-
默認配置:
memory_limit = 128M
2. 快速定位
-
開啟詳細日志
display_errors = On log_errors = On error_log = /var/log/php/error.log
-
復現錯誤:上傳同一張圖片,觀察
Peak memory
(見 §4)。
3. 為什么 5 MB 的圖片能耗盡 128 MB?
核心原因:GD 庫在解碼 / 縮放時會把整張圖片展開到內存,按 4 byte/像素 計,再疊加中間緩沖。
3.1 粗略估算公式(GD)
memory ≈ 寬 × 高 × 4 × 1.65
-
4
:32 bit 色深 -
1.65
:經驗系數,包含縮放 & 額外緩沖
分辨率 | 文件體積*1 | 估算內存 | 128 MB 足夠嗎 |
---|---|---|---|
3840×2160 (4 K) | ≈5 MB | 3840×2160×4×1.65 ≈ 54 MB | ?? |
6000×4000 (24 MP)*2 | ≈5 MB | ≈ 158 MB | ? |
*1 JPEG 在磁盤上是壓縮數據,跟解碼內存無關。
*2 手機/單反隨手拍常見 4–8 MB,但分辨率高達 20 ~ 30 MP。
4. 實際峰值監控
在 upload.php
適當位置插入:
register_shutdown_function(function () {error_log('Peak memory: ' . round(memory_get_peak_usage(true) / 1048576, 2) . ' MB');
});
再次上傳,日志輸出:
Peak memory: 163.14 MB
驗證了公式推算。
5. 解決過程
-
調高
memory_limit
memory_limit = 512M
重啟 PHP-FPM:
sudo systemctl restart php-fpm
再次上傳,問題消失,峰值 163 MB 以內,留足裕量。
-
同步調整上傳相關參數
upload_max_filesize = 50M post_max_size = 100M max_execution_time = 300
6. 最佳實踐與防御措施
措施 | 說明 | 建議級別 |
---|---|---|
限制分辨率 | Responsive Filemanager 支持 $image_max_width / $image_max_height | ???? |
使用 Imagick | extension=imagick ,解碼時按實際色深,內存占用可降 40–60 % | ??? |
異步生成縮略圖 | 上傳→消息隊列→Worker 處理,避免前端線程內存峰值 | ??? |
動態內存預算 | memory_limit ≈ 最大像素 × 4 × 1.65 × 并發系數 | ?? |
壓縮上傳 | 前端或 App 先做分辨率壓縮至 4 K 以內 | ?? |
7. 總結
-
根因:圖片分辨率 決定解碼峰值,而非磁盤體積。
-
經驗閾值:常見 24 MP 原圖解碼需 ~160 MB;并發 2 條就逼近 256 MB。
-
最終 fix:將
memory_limit
提升至 512 MB 并優化上傳策略,系統穩定運行至今。
如果你的線上環境仍保持默認 128 MB,而站點允許上傳手機原圖或單反照片,最好立即評估并調優內存策略。