Workerman4内存持续上涨主因是逻辑死亡对象被循环引用、静态变量累积、闭包捕获或SDK未释放钉住,RSS飙升而memory_get_usage()稳定;需监控RSS与memory_get_peak_usage()线性增长、检查static变量清理、改用WeakMap、避免闭包强引用、显式释放SDK资源并手动触发gc_collect_cycles()验证回收。

Workerman4 服务内存持续上涨,不是“PHP堆没涨”,而是对象逻辑死亡但强引用链仍在——memory_get_usage() 看似稳定,RSS 却一路飙升,这基本是循环引用、静态变量累积、闭包捕获或第三方SDK未释放导致的典型泄漏。
盯紧 RSS 和 peak_usage,别信单次 memory_get_usage()
操作系统 RSS(常驻集大小)才是真实内存水位。用 ps aux --sort=-%mem | head -10 定位具体哪个 Worker 进程在涨;同时在关键位置(如 onMessage 结束、onClose 前)打点:echo memory_get_peak_usage(true), "\n";
重点看该值是否随请求次数线性上升。若每次压测 1 万次后 peak_usage 都+5KB以上,说明有对象持续累积未回收。
查静态/单例容器是否只增不减
搜索代码中所有 static $xxx = []、self::$cache、protected static $instances 等声明,重点关注:
- 是否在 onConnect/onMessage 中无条件执行
[]=或$arr[$id] = $obj - 是否在 onClose 中配对执行
unset(self::$connections[$connection->id])(键必须是标量 ID,不能是 $connection 对象本身) - PHP 8.0+ 强烈改用
new WeakMap()替代静态数组存储连接上下文 - 避免用 Request/Response 实例作缓存键或存入静态属性——它们自带上传文件、原始 body 等大内存块
抓闭包、定时器和 SDK 的隐式强引用
闭包会悄悄把整个作用域对象钉在内存里:
- 禁止写
Timer::add(1, [$this, 'handle'])或function () use ($request) { ... } - 定时器回调优先用静态方法或独立函数;必须用实例方法时,确保 onWorkerStop 或 onClose 中调用
Timer::del($timerId) - Redis/HTTP/SDK 初始化不要放在构造函数或 onWorkerStart 里直接 new;改用懒加载 + isConnected() 判断,断连后显式
unset($this->client) - EasyWeChat、阿里云 SDK 等务必调用
close()或destroy(),不能只靠析构函数
主动触发 GC 并验证回收效果
Workerman 默认开启 GC,但某些扩展或启动脚本可能误调 gc_disable()——请检查入口文件。日常调试建议:
- 在 onMessage 处理末尾加
gc_collect_cycles(); - 配合
gc_mem_caches()清理内部缓存 - 用
xdebug_debug_zval('var_name')查 refcount 和 is_ref,确认可疑对象是否被意外强引用 - 若 gc_collect_cycles() 后 RSS 仍不回落,基本可断定存在跨生命周期的强引用链(如 SwooleTable 混用、全局单例绑定、C 扩展资源未释放)

















