真内存泄漏需同时满足:存在static/单例/global长生命周期数组,且该数组无限追加数据从不清理;否则属正常内存复用或假性溢出。

Webman 内存持续上涨不是“脚本写错了”,而是常驻进程模式下引用没断、缓存没清、GC 没触发的必然结果。直接调大 memory_limit 只会掩盖泄漏,真要稳住 RSS,得从生命周期干预入手。
怎么确认是真内存泄漏,不是假性溢出
看到 Allowed memory size of XXX bytes exhausted 别急着改配置——先看是不是 xdebug、var_dump() 或大文件临时加载在捣鬼。
- 用
php -d zend_extension= -f start.php status关掉 xdebug 后压测,错误是否消失 - 在
onRequest入口加echo "Peak: " . memory_get_peak_usage(true) . "\n";,连续 30 秒请求后对比前后值,看是否线性增长 - 检查
app/Listener/和app/Middleware/里有没有static $cache = []这类结构,且只写不删 - 运行
php start.php status,观察 Summary 中的RSS(常驻集大小)是否随请求次数持续上升且不回落
memory_limit 在 Webman 里到底该在哪设
Webman 运行在 CLI 守护模式,.htaccess 完全无效;ini_set() 必须放在 start.php 最顶部,否则可能被自动加载提前触发而失效。
- 首选:在
start.php第一行加ini_set('memory_limit', '512M');(注意:不能高于 php.ini 硬上限) - PHP-FPM 部署时,改对应 pool 的
www.conf,加php_admin_value[memory_limit] = 384M - CLI 启动时强制指定:
php -d memory_limit=1G start.php start -d - 绝对不要设
-1,它不会让 PHP 真的无限用内存,反而会让 OOM Killer 杀掉进程
哪些 static 数组最容易吃光内存
Webman 中最典型的泄漏源是中间件或监听器里无清理机制的 static 数组,比如日志缓冲、请求 ID 映射、计数器等——它们在进程生命周期内始终强引用,GC 根本收不走。
- 搜索全部
static $data = []、protected static $cache等声明,重点看是否在每次请求中执行[]=或array_push() - 把无限制追加改成带容量上限的循环队列:
if (count(self::$cache) > 1000) { array_shift(self::$cache); }+self::$cache[] = $item; - 禁用裸数组缓存,改用
Webman\Support\Cache::remember()并设 TTL,或直接上 Redis - 若该数组只是临时记录单次请求数据,确保在中间件
after或控制器返回前unset(self::$cache[$key])
闭包、循环引用和 GC 怎么配合才有效
Webman 里闭包捕获 $this、$connection 或整个 $request 是高频泄漏点,尤其在定时器、onMessage 回调中——GC 对这类强引用链基本失效。
- 查所有
Worker::onWorkerStart、onConnect、onMessage注册的匿名函数,看是否use ($this)或use ($request) - 必须捕获对象时,改用
WeakMap(PHP 8.0+):$map = new WeakMap(); $map[$obj] = $data;,对象销毁后引用自动解除 - 在
onClose或任务结束回调里手动触发:gc_collect_cycles(); gc_mem_caches();,再打点看memory_get_peak_usage(true)是否回落 - 避免
foreach ($arr as &$item)后忘记unset($item),残留引用会让最后一个元素无法释放
真正难处理的从来不是 memory_get_usage() 上涨,而是 RSS 不降——那说明有隐藏强引用钉住了内存。别依赖“等它自己回收”,得主动切引用、限容量、设阈值重启。一个没清理的 static 数组,跑三天就能吃掉 2GB。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











