Workerman报“Out of memory”需结合进程生命周期管理、PHP配置分层控制与主动释放三者处理;其memory_limit在常驻进程中仅启动时生效,须在start.php开头或CLI参数中设置,onMessage内ini_set无效。

Workerman 报 “Out of memory” 不能只调 memory_limit,它本质是长连接常驻进程的内存累积问题,必须结合进程生命周期管理 + PHP 配置分层控制 + 主动释放三者处理。
Workerman 的 memory_limit 生效逻辑和陷阱
Workerman 是常驻内存的守护进程,启动后不会像普通 PHP 脚本那样每次请求重置内存。这意味着:ini_set('memory_limit', '512M') 在 Worker 启动后调用基本无效(除非在 onWorkerStart 中且早于任何业务逻辑);而 CLI 模式下用 php -d memory_limit=1G start.php 才是真正起效的第一步。
常见错误是把 Web 环境的配置思路套到 Workerman 上:比如在 onMessage 回调里反复 ini_set(),这不仅不生效,还会干扰 GC 判定。
- 必须在
start.php文件最开头(<?php后立即)或php -d启动参数中设定 - 若用 Docker,还要同步限制容器内存上限(如
-m 512m),否则 PHP 进程超限会被 OOM Killer 直接杀掉,不报错 - Workerman 默认使用
pcntl_fork派生子进程,每个 Worker 进程都独立继承memory_limit,所以总内存占用 = 进程数 × 单进程限制
为什么调高 memory_limit 后还是 OOM?重点查这些地方
Workerman 的内存泄漏多来自对象长期驻留、资源未释放、缓存无清理机制,而非单纯数据量大。以下几处最容易漏掉:
立即学习“PHP免费学习笔记(深入)”;
- 全局静态数组或
static属性持续[]追加,比如记录客户端连接 ID 却从不清理断连残留 - 数据库连接未显式关闭:
$pdo = null或$stmt->closeCursor()缺失,PDO 内部结果集持续占内存 - 使用 GD 或 Imagick 处理图片后,没调
imagedestroy()或$image->destroy() - WebSocket 场景中,对
$connection->send()大量异步发送未做流控,发送缓冲区堆积导致内存暴涨 - 日志写入未限制频率或大小,
file_put_contents($log, $msg, FILE_APPEND)在高频连接下形成 I/O + 内存双压力
Workerman 场景下的安全内存优化实践
比起硬调上限,更应让每个 Worker 进程“轻装上阵、定期重启”。Workerman 自带 max_memory 和 reloadable 机制可直接利用:
- 在
start.php中设置:Worker::$maxMemory = 128 * 1024 * 1024;(单位字节),达到阈值自动重启该 Worker 进程 - 启用平滑重启:
Worker::setDaemonize(true);+Worker::reloadGracefully();配合信号触发,避免连接中断 - 大对象处理完立刻
unset($bigData);,尤其在onMessage或onClose中;不要依赖 GC 自动回收循环引用 - 用
memory_get_usage(true)在关键节点打点,例如在onMessage开头/结尾各记一次,确认单次消息处理是否稳定增长 - 禁用 xdebug(开发环境除外):
php -d zend_extension=none -d xdebug.mode=off start.php,xdebug 在常驻进程中会指数级拖垮内存
Workerman 的 OOM 很少是单次操作爆掉的,多数是连接数上升后,每个连接悄悄多占几 KB,几十个连接就压垮一个 Worker。监控 memory_get_peak_usage() 和 Worker::$maxMemory 的差值,比盲目调高 memory_limit 更能守住底线。



















