PHP 8.1默认启用GC,通过gc_collect_cycles()可回收循环引用内存;unset后内存不降,触发GC才释放,xdebug_debug_zval可观察refcount=2等细节。

确认 PHP 版本与 GC 状态
先确保你使用的是 PHP 8.1,并检查垃圾回收是否启用:
- 运行 php -v,确认输出含 PHP 8.1.x
- 运行 php -r "echo gc_enabled() ? 'enabled' : 'disabled';",应返回 enabled(默认开启)
- 如需验证详细 GC 配置,可执行 php -r "print_r(gc_status());",会显示当前缓冲区大小、已收集周期数、根缓冲区使用量等关键指标
构造可观察的循环引用场景
写一个命令行脚本,人为制造循环引用并观察 refcount 与内存变化:
- 创建文件 test_gc.php,内容如下:
<?php
// 关闭自动 GC,便于手动控制
gc_disable();
// 创建两个相互引用的数组(典型循环引用)
$a = ['child' => null];
$b = ['parent' => null];
$a['child'] = $b;
$b['parent'] = $a;
echo "unset 前内存: " . memory_get_usage() . " bytes
";
var_dump($a); // 可选:确认结构
unset($a, $b);
echo "unset 后内存(未触发 GC): " . memory_get_usage() . " bytes
";
// 手动触发 GC 并观察效果
$collected = gc_collect_cycles();
echo "GC 回收了 {$collected} 个循环节点
";
echo "GC 后内存: " . memory_get_usage() . " bytes
";
?>
- 保存后执行:php test_gc.php
- 你会看到:unset 后内存几乎不变,调用 gc_collect_cycles() 后内存明显下降,且返回值通常为 2(对应两个 zval 节点)
用 xdebug_debug_zval 观察引用计数细节
若已安装 Xdebug(推荐 PHP 8.1 + Xdebug 3.2+),可直接查看 zval 内部 refcount:
- 在脚本中添加:php -d extension=xdebug.so -r "xdebug_debug_zval('a');"(需先定义 $a)
- 或修改 test_gc.php,在 unset 前插入:xdebug_debug_zval('a');
- 输出类似:a: (refcount=2, is_ref=0) —— 表明 $a 和 $b['parent'] 同时引用它,refcount=2,不会被立即释放
模拟常驻进程中的 GC 行为
CLI 模式下模拟 Swoole 类长连接场景,验证 GC 不自动触发的问题:
立即学习“PHP免费学习笔记(深入)”;
- 运行:php -r "for($i=0;$i<15000;$i++){ $arr[] = ['x'=>str_repeat('a',100)]; } echo '内存峰值: '.memory_get_peak_usage()."\n";"
- 再加 GC 调用:php -r "for($i=0;$i<15000;$i++){ $arr[] = ['x'=>str_repeat('a',100)]; } gc_collect_cycles(); echo 'GC 后内存: '.memory_get_usage()."\n";"
- 对比两次峰值差异,可验证:不调用 GC 时内存持续增长;手动触发后部分内存被回收(尤其含循环引用时更显著)



















