Swoole内存泄漏主因是静态变量、闭包引用和资源未释放,需避免全局数据存储、解耦循环引用、协程后清理资源,并设置worker最大请求重启机制,结合监控工具定期分析内存使用。

Swoole 本身不会直接抛出 OutOfMemoryError,但 Worker 进程持续增长的内存占用最终会触发系统级 OOM Killer 杀进程,或 PHP 主动报 Fatal error: Allowed memory size exhausted —— 这是常驻模型下内存泄漏长期累积的典型结果。
为什么 Swoole 的 static 变量不随请求重置
PHP-FPM 每次请求都重建整个 Zend 执行环境,static 变量自然清空;而 Swoole 的 Worker 进程长期存活,onRequest 回调只是函数调用,static $counter = 0 在首次执行后就固化在进程内存里,后续请求只会复用该变量地址。
常见错误现象:
- 计数器越加越大,重启服务才归零
- 缓存数组不断
push却从不unset,内存曲线持续上扬 - 闭包里引用了外部大对象(如
$pdo实例),导致整块资源无法释放
实操建议:
- 把状态逻辑移到局部作用域:函数内声明
$data = [],而非static $cache = [] - 若必须跨请求共享,改用
Swoole\Table或Redis,别依赖 PHP 变量生命周期 - 检查所有
use ($x)闭包,确认$x不是长生命周期对象(如未关闭的PDO连接)
max_request 设多少才合理
这个参数不是“越高越好”,也不是“设了就万事大吉”。它本质是兜底机制:让 Worker 在处理完指定请求数后退出,由 Manager 进程拉起新进程,强制清理所有 PHP 层残留引用。
性能与稳定性权衡点:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- 设太低(如
100):频繁 fork 新进程,CPU 和上下文切换开销上升 - 设太高(如
10000):泄漏积累时间过长,可能在重启前就触发 OOM - 生产环境推荐区间是
500~2000,具体看单请求平均内存增长量 —— 可用memory_get_usage(true)在onRequest开头/结尾打点观测
注意:max_request 对 TaskWorker 无效,需单独配 max_task_request。
哪些资源必须显式 unset 或 close
Swoole 不会自动回收你手动创建的资源句柄,尤其那些底层 C 结构体未被 PHP GC 覆盖的部分。
必须干预的典型场景:
-
PDO/MySQLi连接:不用__destruct,要在onClose或请求结束时调用$pdo->close() -
Swoole\Coroutine\Http\Client实例:即使设置了keep_alive => true,也要在使用完毕后unset($client),否则协程栈残留引用 -
opcache:CLI 模式下若开启opcache.enable_cli=1,脚本字节码会长期驻留,应禁用 - 全局
Table或Atomic实例:虽本身是共享内存,但 PHP 层变量仍持有句柄,unset($table)可减少 PHP 引用计数压力
onWorkerStop 里销毁对象真有用吗
有用,但仅限于你在 onWorkerStart 中初始化的、且生命周期明确绑定到 Worker 的对象。比如自建连接池、全局日志实例、配置缓存等。
容易踩的坑:
- 在
onWorkerStart中 new 的对象,必须在onWorkerStop中unset或调用其destroy()方法,不能只靠 PHP 自动析构 -
onWorkerStop不会在进程异常退出(如被 OOM Killer 杀掉)时触发,所以它不能替代max_request的兜底作用 - 不要在
onWorkerStop里做耗时操作(如写文件、发 HTTP 请求),它会阻塞 Manager 进程拉起新 Worker
真正关键的不是“有没有销毁”,而是“有没有让每个 Worker 的内存增长有明确上限”——这需要结合 max_request、资源显式释放、以及避免静态变量污染三者共同控制。

















