PHP「Not enough Memory」实战排错笔记
目录
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
:经验系数,包含缩放 & 额外缓冲
分辨率 | 文件体积*¹ | 估算内存 | 128 MB 足够吗 |
---|---|---|---|
3840×2160 (4 K) | ≈5 MB | 3840×2160×4×1.65 ≈ 54 MB | ✔️ |
6000×4000 (24 MP)*² | ≈5 MB | ≈ 158 MB | ❌ |
*¹ JPEG 在磁盘上是压缩数据,跟解码内存无关。
*² 手机/单反随手拍常见 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,而站点允许上传手机原图或单反照片,最好立即评估并调优内存策略。