PHP 7.4 下 FPM 进程数过多导致服务器内存溢出怎么排查?

文章导读
PHP 7.4 FPM 进程过多导致内存溢出,通常是因为 `pm.max_children` 设置值乘以单进程内存占用超过了服务器物理内存。排查时应先通过 `ps` 命令计算单进程平均 RSS,再按可用内存的 60% 反推进程上限,同时排除代码层面的内存泄漏。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 常见问题
  7. 参考来源
A A

PHP 7.4 FPM 进程过多导致内存溢出,通常是因为 `pm.max_children` 设置值乘以单进程内存占用超过了服务器物理内存。排查时应先通过 `ps` 命令计算单进程平均 RSS,再按可用内存的 60% 反推进程上限,同时排除代码层面的内存泄漏。

先说结论:解决 FPM 内存溢出不能只调大 `memory_limit`,必须限制进程总数并优化单进程内存占用。

  • 先确认:使用 `ps` 命令查看 php-fpm 进程的实际 RSS 内存占用,而非 VSZ
  • 先处理:根据公式 `pm.max_children ≈ (可用内存 × 0.6) ÷ 单进程平均 RSS` 调整配置
  • 再验证:观察服务器内存水位及 PHP 错误日志,确认不再出现 OOM 或 502 错误

命令速用版

以下命令用于快速计算当前 php-fpm 进程的平均内存占用和总占用,适用于 Linux 环境:

# 计算 php-fpm 进程总 VSZ 占用(虚拟内存)
ps -o pid,vsz,comm -C php-fpm | tail -n +2 | awk '{sum+=$2} END {printf "%.0f MB\n", sum/1024}'

# 计算 php-fpm 进程平均 RSS 占用(物理内存,更准确)
ps -o pid,rss,comm -C php-fpm | tail -n +2 | awk '{sum+=$2} END {printf "%.0f MB\n", sum/NR/1024}'

# 查看当前 php-fpm 进程数量
ps aux | grep php-fpm | grep -v grep | wc -l

为什么会这样

FPM 内存溢出的本质是后端处理能力跟不上请求量,导致进程堆积或单进程占用过高。PHP-FPM 采用多进程模型,服务器总内存消耗等于“进程数 × 单进程内存占用”。如果 `pm.max_children` 设置过大,或者单个脚本存在内存泄漏,总占用会迅速突破物理内存上限,触发系统 OOM Killer 杀死进程或导致服务不可用。公开资料中没有看到可靠的量化数据表明特定版本 PHP 7.4 的固定内存基线,实际占用取决于加载的扩展和业务代码。

分步处理

第一步:测量单进程真实内存占用
不要依赖配置文件中的估算值,使用 `ps` 命令查看实际运行中的 RSS 值。若均值超过 120MB,优先关闭没用的扩展(如 imagick、xdebug),比硬调 `pm.max_children` 更治本。

PHP 7.4 下 FPM 进程数过多导致服务器内存溢出怎么排查?

第二步:反推进程数上限
根据公式 `pm.max_children ≈ (可用内存 × 0.6) ÷ 单进程平均 RSS` 计算。例如 2GB 机器实测 RSS 为 110MB 时,建议值是 (2048 × 0.6) ÷ 110 ≈ 11,取整为 12。注意这里是 RSS(实际物理内存),不是 VSZ。

第三步:调整 php-fpm 配置
编辑对应版本的 `php-fpm.d/www.conf` 路径(例如 `/opt/phpenv/versions/7.4/etc/php-fpm.d/www.conf` 或 `/etc/php-fpm.d/www.conf`)。设置 `pm = dynamic`,不推荐低配机器使用 `ondemand` 模式,因为首屏加载可能慢 2–3 秒。确保 `pm.min_spare_servers ≥ 2`,避免空闲时进程归零。

第四步:排查代码内存泄漏
在可疑逻辑前后插入 `memory_get_usage(true)` 和 `memory_get_peak_usage()` 监控。重点排查 `require`、`json_decode`、`file_get_contents` 或循环中的 `array_push`。若 `json_decode` 后内存暴涨几倍,说明结构嵌套太深或存在循环引用。

PHP 7.4 下 FPM 进程数过多导致服务器内存溢出怎么排查?

怎么验证是否生效

调整配置后重启 php-fpm 服务,使用 `htop` 或 `glances` 监控服务器内存使用情况。观察 PHP 错误日志,确认不再出现 `Fatal error: Allowed memory size exhausted`。若配置了 `pm.max_requests`(如设为 200–500),观察子进程是否定期重启,这有助于缓解累积泄漏影响。

常见坑

1. 混淆 memory_limit 与物理内存
`memory_limit` 是单个脚本能用的最大内存,不是服务器总内存。调大它不能解决进程数过多导致的总内存溢出,反而可能加剧问题。

2. 盲目使用 ondemand 模式
很多用户手动切到 `pm = ondemand` 想省内存,结果发现首屏加载慢。因为每个新请求都要 fork 进程,fork 本身在磁盘 IO 差的 VPS 上耗时可达 150ms。

3. 忽略 PHP 写时复制机制
遍历大型数组时,若修改数组值导致遍历过程数组复制一份,会产生分裂,生成新的 HashTable 结构体,导致内存占用瞬间变大。建议采用引用遍历 `foreach($arr as $key => &$item)` 并在遍历完成后 `unset`。

PHP 7.4 下 FPM 进程数过多导致服务器内存溢出怎么排查?

常见问题

pm.max_children 设成多少合适?

没有固定值,必须根据公式计算。设成 5 确实不崩,但并发一过 10 就 502;设成 50 在 2GB 机器上可能刚启动就 OOM。

开启 opcache 会加剧内存溢出吗?

启用 opcache 扩展可以减少脚本的编译次数,从而减少内存使用和提高性能。但若 `opcache.max_accelerated_files` 设得过大,或大量动态生成文件,缓存区会快速占满,导致整体进程 RSS 飙升。

如何防止代码内存泄漏累积?

生产环境可配置 PHP-FPM 的 `pm.max_requests`,让子进程定期重启。日常开发中,在 CI 流程加入内存快照检查,比对对象实例数变化趋势。

参考来源

  • phpEnv 解决 PHP-FPM 进程过多导致的资源占用问题
  • Nginx 负载均衡指南:解决后端 PHP-FPM 进程池溢出问题
  • php 内存溢出怎么排查_php 内存限制调试与优化方法
  • PHP 网站搭建内存泄漏:如何排查与解决 PHP 脚本的内存溢出
  • 关于 PHP 进程防止内存溢出的排查