FrankenPHP CLI 模式下必须手动调用 gc_collect_cycles() 以防止内存持续上涨,因其无请求生命周期终结点导致循环引用对象长期滞留;Web 模式下则由请求结束自动触发 GC。

FrankenPHP CLI 模式下 gc_collect_cycles() 必须手动调用
FrankenPHP 在 CLI 模式(比如执行队列任务、长时数据同步脚本)中,gc_collect_cycles() 不是“可选优化”,而是防止内存持续上涨的必要操作。原因和 PHP 原生 CLI 完全一致:没有请求生命周期终结点,根缓冲区(gc.root_buffer_size 默认 10,000)极难自动填满到半满(5,000)阈值,导致循环引用对象长期滞留。
常见错误现象包括:
- 脚本运行数小时后
memory_get_usage(true)持续缓慢上升,且不回落 -
gc_status()['roots']长期 > 0 并缓慢增长 - 明明反复
unset($bigArray)或销毁对象,但内存没明显释放
什么时候调用 gc_collect_cycles() 才有效
不是按时间间隔(如每 5 秒一次),而是按“内存释放节奏”嵌入关键节点:
- 每处理完一批数据后调用,例如:每处理 100 条数据库记录后执行
gc_collect_cycles() - 在显式
unset($largeObject)或清空大数组(如$cache = [])之后立即调用,避免它们卡在根缓冲区里等待下次自动触发 - 避免在高频循环体内调用(如每次 foreach 迭代都调),GC 本身有 CPU 开销,会显著拖慢吞吐
- 不要依赖
gc_enable()/gc_disable()切换——FrankenPHP 默认已启用 GC,禁用只会让泄漏更隐蔽
gc_collect_cycles() 返回 0 不代表没垃圾
这个函数只清理“循环引用结构”,返回值是本次实际回收的循环对象数量:
立即学习“PHP免费学习笔记(深入)”;
- 返回
0:说明当前根缓冲区里没发现可回收的循环引用,但不代表内存没泄漏(可能是单向不可达大对象,比如全局缓存未清空) - 返回
> 0:确认有循环引用被清理,配合memory_get_usage(false)对比能观察到内存下降 - 如果返回始终为 0,但内存还在涨,问题大概率不在 GC,而在你没
unset()或没断开强引用(如对象属性仍 hold 着大数组)
FrankenPHP Web 请求模式下一般不用手动 GC
FrankenPHP 的 HTTP 请求模型(类似 FPM)会在每个请求结束时自动执行一次兜底 GC 扫描,只要不滥用全局静态变量或跨请求缓存,通常不需要干预。但要注意:
- 如果你在中间件或事件监听器里长期持有大对象(如未释放的 PDOStatement、未关闭的资源句柄),GC 也救不了——得靠及时
unset()或作用域控制 - 自定义会话处理器中的
gc回调函数,和 Zend GC 是两套机制,别混淆;它只清理过期 session 数据,不参与 zval 循环引用回收 - FrankenPHP 的 SAPI 层未修改 PHP 原生 GC 行为,所有 PHP 8.1+ 的 GC 规则完全适用
gc_collect_cycles() 更关键。



















