webman内存越跑越高是因为worker进程常驻,全局变量、静态属性、闭包引用等持续驻留,php gc在循环引用等场景下难以及时回收;必须主动清理、切断引用链、限制单次处理规模。

Webman 是常驻内存的 PHP Swoole 框架,内存不自动释放是常态;靠“等脚本结束”根本不管用,必须主动干预生命周期、切断引用链、限制单次处理规模。
为什么 Webman 内存会越跑越高
Webman 启动后 Worker 进程长期存活,所有全局变量、静态属性、闭包捕获、未清理的缓存、数据库连接池中的闲置句柄,都会持续驻留。PHP 的 GC 在常驻进程中触发不及时,尤其遇到循环引用(比如模型关联 + 事件监听器互相持有时),gc_collect_cycles() 可能都收不回内存。
- 常见现象:
memory_get_usage()持续上涨,memory_get_peak_usage(true)明显高于预期,重启 Worker 后立即回落 - 典型诱因:在
onRequest中 new 大对象、把请求数据塞进static::$cache、用use ($request)绑定整个 Request 实例到定时器回调里 - 和传统 PHP-FPM 最大区别:没有“请求结束即重置”的兜底机制,每个 Worker 的内存是累加的
每次请求后必须手动清理的变量类型
不能依赖 GC 自动回收,要在中间件末尾或控制器返回前显式切断引用。
- 大数组或 JSON 解析结果:处理完立刻
unset($rawData),别让它活过当前请求周期 - 文件内容:用
file_get_contents()读取后,unset($content);改用SplFileObject流式读取更安全 - 数据库查询结果:避免
$users = $query->all()全量加载;改用$query->cursor()或分批chunk(500) - 闭包中
use的变量:若只读,用use ($id);若需对象,确认该对象没反向引用当前作用域,否则改用 ID + 延迟查库 - 静态缓存类:如自定义
Cache::set('key', $bigArray),务必配套Cache::forget('key')或设置 TTL,禁用永久缓存
生成器(yield)在 Webman 中的实际落地点
Webman 支持原生 yield 返回响应体,这是降低峰值内存最直接的手段,尤其适合导出、流式 API、大列表渲染。
- 不要写:
return json(['list' => User::all()])—— 全部用户一次性转数组+JSON编码,内存翻倍 - 应该写:
return response()->stream(function () { foreach (User::cursor() as $user) { echo json_encode($user) . "\n"; } }) - 配合数据库游标:
User::query()->select('id','name')->cursor(),避免OFFSET性能退化 - 注意:Swoole 的
response()->stream()不支持 chunked transfer encoding 自动分块,需手动ob_flush()+flush()(但 Webman 封装已处理)
OPcache 和内存限制的协同配置
Webman 的入口文件(start.php)只执行一次,OPcache 对它收益极大;但 memory_limit 设置不当反而掩盖泄漏。
- OPcache 必开:
opcache.enable=1,opcache.memory_consumption=256(单位 MB),opcache.max_accelerated_files=20000 - memory_limit 建议设为固定值(如
256M),**不要设-1** —— 单个 Worker 跑飞会拖垮整机 - 在
start.php开头加:ini_set('memory_limit', '256M'),确保所有 Worker 统一 - 监控关键点:在
WorkerStart和每个请求的onRequest开头/结尾打点,记录memory_get_usage(),定位哪类请求最耗内存
Webman 的内存问题很少出在框架本身,而在于开发者把“一次性的 PHP 思维”带进了常驻进程——变量不 unset、缓存不清理、对象不复用、数据不流式,这些习惯在 Webman 里会被指数级放大。真正有效的调优,是从第一个 new 和最后一个 unset 开始的。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











