Workerman内存持续上涨需立即干预,因RSS每分钟涨超2MB且memory_get_peak_usage(true)同步攀升,10~30分钟后将被OOM Killer杀死;确认泄漏需观测RSS线性上升趋势并结合gc_collect_cycles()前后峰值差值>4MB,再通过strace、内存打点和xdebug_debug_zval定位闭包捕获、静态变量未清理及第三方SDK残留等强引用问题。

Workerman启动后内存持续上涨,说明进程在运行过程中未能及时释放已死亡对象,RSS值每分钟上涨超2MB且memory_get_peak_usage(true)同步攀升,此时必须立即干预,否则10~30分钟后进程大概率被OOM Killer强制杀死,连接拒绝率飙升。
确认是否真泄漏而非正常增长
执行ps aux --sort=-%mem | grep php,筛选出运行超5分钟的Worker进程,记录其RSS(单位KB);每2分钟采样一次,连续观测10分钟。若RSS呈线性上升趋势(如从120MB→140MB→160MB),且在onMessage回调开头插入echo memory_get_peak_usage(true)."\n"; gc_collect_cycles();后,两次打印值差值稳定大于4MB,则确认为真实泄漏。
【不要依赖memory_get_usage()单次返回值】,它只反映当前脚本堆内存,完全无法体现闭包捕获、静态变量、Redis连接池等底层扩展持有的“僵尸对象”。
定位泄漏源头的三步法
第一步:用strace -tt -p PID观察进程行为。若每秒刷出数十行epoll_pwait且返回值恒为0或毫秒级超时,说明事件循环空转——这往往不是业务代码问题,而是内核层面连接关闭风暴或全连接队列溢出引发的sys CPU高,需同步检查net.core.somaxconn和backlog配置。
第二步:在onMessage、onClose、定时器回调入口处插入gc_collect_cycles()并记录memory_get_peak_usage(true),对比两次调用差值。若某次回调后峰值猛增5MB以上,该回调即为高危区。
第三步:对可疑对象执行xdebug_debug_zval($obj)(需启用Xdebug),重点看refcount是否异常高(如≥3)、is_ref是否为1。若一个TcpConnection对象refcount卡在4且无法下降,基本可断定它被闭包→类实例→静态数组三级强引用锁死。
高频泄漏点检查与修复
方法一:静态变量清理缺失
检查所有self::$connections、static $cache = []类定义,在onClose中是否执行unset(self::$connections[$connection->id]);。【键必须是标量ID,绝不可用$connection对象本身作键】,否则unset()无效。
方法二:闭包隐式捕获
将Timer::add(1, function() use ($this) { })全部替换为Timer::add(1, [$this, 'tick'])或独立静态函数;【Timer::add传数组回调比匿名函数更危险——它给实例加了一条强引用链】。
方法三:第三方SDK残留连接
Redis等SDK须显式close()再unset($this->redis);SwooleTable必须在Worker::onWorkerStart中创建,且仅在该回调内使用global $table;EasyWeChat等SDK初始化后,务必在onWorkerStop中调用destroy()或清空静态属性。
PHP 8.0+强制用WeakMap替代static array存储连接上下文,避免因强引用导致GC失效。











