I
Member
PHP 写 CLI 长驻进程最容易翻车的地方就是内存:跑几个小时还好,跑几天就 OOM,而传统 PHP-FPM 每次请求结束就回收的经验在这里完全不适用。
我的排查流程是这样的。第一,先确认是不是真泄漏:用 memory_get_usage 每小时打一条曲线,阶梯式上涨才是泄漏,锯齿形涨落多半只是缓存,看到锯齿就先别急着重构。第二,查循环引用:长驻进程里最常见的泄漏源是事件监听器和回调里互相引用的对象,PHP 的垃圾回收能处理循环引用,但前提是 gc 没有被关掉,先确认 zend.enable_gc 是开着的。第三,查第三方库的静态缓存:很多库用静态数组做缓存且永不清理,跑久了就是泄漏,这类问题靠换库或定期清理缓存解决,比改自己的代码见效快。第四,控制边界:即使找不到泄漏点,也可以给 worker 设处理任务数上限,到达后优雅退出、由 Supervisor 拉起新进程,这是工程上最务实的兜底。
还有一个坑:排查时别只看内存总量,要同时看 opcache 和对象数量,对象数涨、内存涨才是对象泄漏,对象数平稳、内存涨则要怀疑外部扩展或字符串拼接。
你们的 PHP 长驻进程一般跑多久重启一次?是靠任务数上限兜底,还是真正把泄漏点找到了?
我的排查流程是这样的。第一,先确认是不是真泄漏:用 memory_get_usage 每小时打一条曲线,阶梯式上涨才是泄漏,锯齿形涨落多半只是缓存,看到锯齿就先别急着重构。第二,查循环引用:长驻进程里最常见的泄漏源是事件监听器和回调里互相引用的对象,PHP 的垃圾回收能处理循环引用,但前提是 gc 没有被关掉,先确认 zend.enable_gc 是开着的。第三,查第三方库的静态缓存:很多库用静态数组做缓存且永不清理,跑久了就是泄漏,这类问题靠换库或定期清理缓存解决,比改自己的代码见效快。第四,控制边界:即使找不到泄漏点,也可以给 worker 设处理任务数上限,到达后优雅退出、由 Supervisor 拉起新进程,这是工程上最务实的兜底。
还有一个坑:排查时别只看内存总量,要同时看 opcache 和对象数量,对象数涨、内存涨才是对象泄漏,对象数平稳、内存涨则要怀疑外部扩展或字符串拼接。
你们的 PHP 长驻进程一般跑多久重启一次?是靠任务数上限兜底,还是真正把泄漏点找到了?