Webman内存持续上涨是因为Worker常驻导致static属性、全局变量、闭包引用等无法自动释放,PHP GC在循环引用等场景下难以及时回收,必须手动清理、切断引用链、限制单次处理规模并合理使用yield和Redis连接池。

Webman 的高性能不是靠“更快的语法”,而是靠彻底绕开 PHP-FPM 的生命周期枷锁——它让每个 Worker 进程常驻内存,跳过每次请求重复加载、初始化、销毁的全过程。这意味着你写的代码只加载一次,但变量、连接、缓存会一直活着,用不好就内存越跑越高。
为什么 Webman 启动后 memory_get_usage() 持续上涨
Worker 进程不退出,static 属性、全局数组、未 unset 的大对象、闭包 use 的整个 $request 实例,都会持续累积。PHP GC 在常驻进程里触发不及时,尤其遇到模型和监听器互相持有(循环引用),gc_collect_cycles() 都可能收不回。
- 典型现象:
memory_get_peak_usage(true)比预期高 2–3 倍,重启 Worker 后立即回落 - 常见诱因:
static::$cache = $bigArray、new \App\Model\User()后没释放、定时器回调里use ($request) - 和 PHP-FPM 的本质区别:没有“请求结束即重置”的兜底机制,内存是累加的,不是自动清零的
哪些变量必须在 onRequest 结束前手动清理
不能依赖 GC 自动回收,必须在中间件末尾或控制器返回前显式切断引用链。
- 大数组或 JSON 解析结果:
unset($rawData),别让它活过当前请求周期 - 文件内容:
file_get_contents()后立刻unset($content);更安全的做法是改用SplFileObject流式读取 - 数据库查询结果:避免
$users = $query->all()全量加载;改用$query->cursor()或chunk(500) - 闭包中
use的变量:只读用use ($id);若需对象,确认它没反向引用当前作用域,否则改用 ID + 延迟查库 - 静态缓存类:
Cache::set('key', $bigArray)必须配套Cache::forget('key')或设置 TTL,禁用永久缓存
yield 返回响应体是降低峰值内存最直接的手段
Webman 原生支持 yield 作为响应体,把大列表、导出文件、流式 API 的内存压力从“全量加载进内存”降到“边生成边发送”。
立即学习“PHP免费学习笔记(深入)”;
- 适合场景:Excel 导出、千万级日志拉取、长轮询响应、大图拼接服务
- 示例:
return function () { foreach ($rows as $row) yield json_encode($row) . "\n"; }; - 注意:不要在
yield函数里引用外部大对象,否则整个闭包生命周期内该对象无法被释放
Redis 连接池不是可选项,而是内存与稳定性的生死线
在 Worker 常驻前提下,每次 new Redis() 会导致连接数爆炸、TIME_WAIT 堆积、Redis 服务端拒绝新连接。
- 必须用
webman/redis扩展(基于 predis 或 phpredis 封装),它自动管理连接池 - 配置
config/redis.php中的'pool' => ['min_connections' => 2, 'max_connections' => 20] - 业务中直接调用
Redis::get('key'),底层自动取用/归还连接 - 严禁在
onWorkerStart里new Redis()并赋值给全局变量,多进程间共享连接会引发序列化失败或连接污染
真正难的不是“怎么写”,而是判断哪些变量该活多久——常驻进程里,每个 static、每个 use、每个没 unset 的数组,都在悄悄延长生命周期。内存不会自己喊疼,但峰值一到,OOM 就是唯一结局。



















