workerman协程内存泄漏需立即干预,通过rss持续上涨和memory_get_peak_usage打点确认泄漏;强制回收需在onclose调用gc_collect_cycles()、try-finally中unset大对象并gc、避免闭包捕获;清理channel未关闭、静态数组用对象作键、异步连接未释放三大高频泄漏点。

Workerman协程内存占用过高会导致协程调度变慢、连接响应延迟飙升,甚至触发OOM Killer强制杀进程,必须在RSS持续上涨时立即干预,不能等服务卡死才动手。
确认是否真泄漏而非协程栈正常增长
执行ps aux --sort=-%mem | grep php,挑出运行超5分钟的Worker进程,记录其RSS值(单位KB);连续观测8分钟,若RSS每分钟涨3MB以上,且memory_get_peak_usage(true)打点同步攀升,则确认为泄漏;若仅启动后1分钟内快速上涨至稳定值(如从8MB升到22MB后不再动),大概率是协程栈预分配或opcache加载所致,无需干预。
【不要依赖memory_get_usage()单次返回值】,它只反映当前脚本堆内存,完全无法体现协程中Fiber对象、Channel缓冲区、静态数组持有的“僵尸引用”。
强制回收协程残留资源
第一步:在onClose回调末尾插入gc_collect_cycles(),确保连接断开后立即触发GC。
第二步:对每个协程启动点加try...finally块,finally里显式unset所有大数组、PDOStatement、Redis连接实例,并调用gc_collect_cycles()。
第三步:检查所有Coroutine::create()调用,若回调内用了use ($connection)或use ($this),立刻改用数组回调[$this, 'handle']或独立静态方法——闭包捕获会把整个对象实例钉在内存里,GC永远扫不到。
清理高频泄漏点
方法一:Channel未关闭导致缓冲区堆积
检查所有new Channel(1024)创建点,在协程退出前必须执行$channel->close(),否则缓冲区内存永不释放。特别注意在throw或return前漏掉close调用。
方法二:静态数组键用对象而非ID
若用self::$cache[$connection] = $data,$connection对象作键会导致unset无效;必须改为self::$cache[$connection->id] = $data,并在onClose中unset(self::$cache[$connection->id])。
方法三:协程内未释放异步连接
使用AsyncTcpConnection或workerman/mysql时,必须在协程结束前调用$conn->close()再unset($conn),否则底层socket句柄和缓冲区一直挂着。











