跳转到主内容
趣航编程网 - 趣学编程,启航技术之路!

为什么Hyperf项目部署后内存持续升高_利用Swoole Tracker排查内存泄漏

Hyperf内存持续升高基本可断定为内存泄漏,需用swoole_tracker捕获malloc/free不匹配,结合ps aux观察RSS线性上涨趋势,重点排查dispatch_mode配置、Context未清理、静态变量累积及C扩展内存持有问题。 Hyperf项目部署后内存持续升高,基本可以断定是内存泄漏,而不是“偶发卡顿”或“瞬时高峰”。关键不是猜原因,而是快速确认泄漏是否存在、定位到具体模块——
swoole_tracker
是目前最直接有效的工具,它能捕获 C 层 malloc/free 不匹配,而这是
memory_get_usage()
和 PHP 日志完全看不到的盲区。 看 RSS,别信 memory_get_usage() 协程常驻进程下,
memory_get_usage(true)
只反映 Zend 堆内存,漏掉了协程栈、Redis/PDO 扩展持有的 C 层内存、Swoole 自身 malloc 分配。你看到“用了 12MB”,实际
ps aux
显示 RSS 已涨到 320MB——差额就是泄漏本体。 用
ps aux --sort=-rss | head -5
每 30 秒观察,RSS 是否线性上涨 在请求末尾加
gc_collect_cycles(); gc_mem_caches();
,等 5 秒再看 RSS 是否回落;不回落,说明存在强引用未断(比如闭包捕获大对象、静态变量存了 Request 实例) 不要依赖日志里打印的
memory_get_usage()
值做判断,它对泄漏排查几乎无效 swoole _tracker 配置必须关掉 tracker.enable
tracker.enable
和
tracker.enable_malloc_hook
不能同时开启,否则 hook 失效。线上复现阶段必须关闭总开关,只启用 malloc hook 和 memcheck。 正确配置片段:
tracker.enable=0
、
tracker.enable_malloc_hook=1
、
tracker.enable_memcheck=1
采样率别设 100:调试时用
tracker.sampling_rate=10
,上线环境严禁全量采样,否则性能暴跌 泄漏日志默认输出到
/tmp/swoole_tracker.log
,典型行如
[leak] addr=0x7f8b4c0a1234 size=16384 at ext/redis/redis.c:1234
,直接定位到 C 文件行号 Worker0 内存独高?先查 dispatch_mode 如果只有 Worker0 RSS 持续飙升,其他 Worker 正常,大概率不是代码泄漏,而是配置误配导致流量全部打到 Worker0。 Swoole 6.1.1 Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。 下载 检查
server.settings.dispatch_mode
:值为
2
(即
SWOOLE_DISPATCH_QUEUE
)时,所有请求会发往 Worker0,造成单点内存暴涨 应改为
1
(
SWOOLE_DISPATCH_FDMOD
)或
3
(
SWOOLE_DISPATCH_IPMOD
),让负载均衡分发 加日志时务必带上
posix_getpid()
,避免多 Worker 日志混杂失焦,例如:
$this->logger->info('SQL executed', ['pid' => posix_getpid(), 'sql' => $sql])
协程退出 ≠ 内存释放,Context 和静态变量是重灾区 协程结束时,栈内存自动回收,但堆内存里的 Context 数据、静态属性、未 close 的协程句柄仍长期驻留。
Context::set('big_data', $hugeArray)
后没配
defer
清理,协程退出后数据还在 类中定义
public static $cache = []
,每次请求都
$cache[] = $item
,越积越多 自定义进程中用
Coroutine::create()
启协程,却忘了
Coroutine::close()
收尾 验证方式:在关键节点插入
echo memory_get_usage(true), "\n";
,但仅作辅助,最终以 RSS 变化为准 真正难排查的泄漏,往往藏在 C 扩展调用链里(比如 Redis::hGetAll 返回大数组后被静态变量持有),
swoole_tracker
是唯一能穿透 PHP 层直达 malloc 调用栈的工具。别在 Hyperf 日志里翻来覆去找“谁没 unset”,要去看协程真实生命周期和内存分配路径——那才是泄漏发生的现场。

相关文章