应监控 RSS 而非 memory_get_usage(),因后者仅反映 PHP 堆内存,不包含协程栈、C 层 malloc 及扩展内存;RSS 持续上升且 GC 后不回落,表明存在强引用泄漏,需结合 gc_collect_cycles()、swoole_tracker 及软性熔断定位防控。

如何用 memory_get_usage() 和 RSS 监控真实内存增长
只看 memory_get_usage() 容易误判——它返回的是 PHP 堆内分配量,不包括底层 C 扩展(如 Swoole、PDO)、共享内存或未归还给操作系统的空闲页。真正该盯的是进程的常驻内存(RSS),它反映实际物理内存占用。
推荐组合监控方式:
- 每处理完 10–50 个请求后,调用
gc_collect_cycles()+gc_mem_caches(),再记录memory_get_usage(true) - 用
ps -o rss= -p $PID或top -p $PID -b -n1 | tail -1每 30 秒抓一次 RSS 值,画趋势图 - 若 RSS 持续单向上涨(比如每小时 +5MB),且 GC 后无回落,基本可判定存在强引用泄漏
为什么 static::$cache 是 Webman 内存泄漏头号诱因
Webman 的 Worker 进程不死,static 属性生命周期等同于进程本身。一旦业务代码在中间件或控制器里执行了 static::$cache[$key] = $bigData 却没配对 unset(static::$cache[$key]) 或 TTL 清理,这个数组就会无限膨胀。
典型错误模式:
立即学习“PHP免费学习笔记(深入)”;
- 用
static::$cache存整个file_get_contents()结果,而不是流式读取 - 把
$request或$response实例塞进static数组(它们自带大量闭包和反向引用) - 在定时器回调中
use ($request),导致$request被长期持有
验证方法:在 onRequest 开头加 var_dump(count(static::$cache));,压测时观察是否随请求数线性增长。
yield 流式响应如何降低峰值内存
Webman 支持原生 yield 返回响应体,这是绕过全量构造 Response 对象最直接的手段。尤其适合导出 CSV、渲染万行列表、转发大文件流——避免一次性把全部数据加载进内存。
示例对比:
/* ❌ 高峰内存风险 */
return response()->html(view('export', ['rows' => $query->all()]));
<p>/<em> ✅ yield 流式,内存恒定在几百 KB </em>/
return response()->stream(function () use ($query) {
foreach ($query->cursor() as $row) {
echo json_encode($row) . "\n";
ob_flush();
flush();
}
});注意:yield 在 Webman 中需配合 response()->stream() 或直接返回生成器,不能裸写 yield 'xxx';否则会触发 Fatal error: Generators cannot be sent to output directly。
何时该主动重启 Worker 而不是硬扛
不是所有内存上涨都要“修”,Webman 的设计允许合理内存复用。关键看两点:
- RSS 是否突破预设软上限(如 256MB),且连续 3 次 GC 后无回落
- 单次请求是否触发
Fatal error: Allowed memory size exhausted—— 这说明memory_limit已被击穿,必须干预
建议在 start.php 中配置自动保护:
Worker::$maxRequest = 1000; // 每个 Worker 处理 1000 次请求后优雅退出 Worker::$maxMemory = 268435456; // 256MB,超限立即重启
这比死磕“绝对不泄漏”更务实——常驻进程框架的稳定性,往往靠可控的生命周期轮换来保障,而非追求零增长。



















