PHP-FPM内存溢出是单次请求瞬时撑爆memory_limit,触发Fatal error并502;Swoole则是常驻进程RSS持续累积致OOM Killer杀进程,需监控RSS而非仅memory_get_usage()。

PHP-FPM内存溢出是“单次请求撑爆进程”
PHP-FPM每个Worker进程只处理一个HTTP请求,生命周期短:加载代码→执行→释放全部内存。内存溢出通常表现为某个请求中 memory_limit 被突破,直接触发 Fatal error: Allowed memory size of XXX bytes exhausted,进程退出,Nginx 返回 502。
常见诱因包括:
- 一次性读取大文件(
file_get_contents($big_file)) - 未分页遍历超大数据集(
foreach ($huge_array as $item)) - 递归过深或闭包循环引用(尤其在 Laravel 的
with()链式调用中误带全量模型)
这类溢出是“瞬时、可复现、隔离性强”的——杀掉当前进程即可恢复,不影响其他请求。
Swoole内存溢出是“长期累积压垮常驻进程”
Swoole进程不随请求结束而销毁,memory_get_usage() 看不到协程栈、C 层 malloc 分配(如 Redis 扩展的连接缓冲区)、第三方扩展持有的资源。真正危险的是 RSS(Resident Set Size)持续上涨,最终被系统 OOM Killer 杀掉,日志里只留 Killed process XXX (php) total-vm:XXXXkB, anon-rss:XXXXkB, file-rss:0kB。
立即学习“PHP免费学习笔记(深入)”;
典型表现有:
-
ps aux --sort=-rss | head -n 5显示某 worker 进程 RSS 从 30MB 涨到 300MB 并不停止 - 重启后 RSS 回落,但几小时内又爬升至临界值
- 加了
gc_collect_cycles()和gc_mem_caches()后 RSS 不降——说明存在强引用未断
这不是某段代码写错了,而是整个生命周期里资源没归还:比如全局静态数组不断 push 请求数据、协程内 new Redis() 后没显式 close()、或监听器注册后忘记 Event::del()。
排查手段完全不同
PHP-FPM 只需开 error_log + display_errors=On,错误堆栈会明确指向哪行代码超限;Swoole 必须盯住 RSS,配合工具链定位:
- 上线前必须关掉
tracker.enable_malloc_hook=0,调试阶段才设为1,否则性能暴跌 -
swoole_tracker报告里的[leak] addr=0x7f8b4c0a1234 size=16384 at ext/redis/redis.c:1234才是真实泄漏点 - 别依赖
max_requests当解药——它只防雪崩,不治泄漏;--max-requests=1000是兜底,不是诊断
最易被忽略的一点:Swoole 中 $_SERVER、$_POST 等超全局变量不可用,但开发者常把 Request 实例存进静态属性,以为“每次新请求都会重建”,实际是同一个对象反复填充——这就是 RSS 悄悄上涨的起点。



















