
php 的 unset() 只减少引用计数(refcount),并不立即触发垃圾回收;只要对象仍有其他有效引用(如静态属性),对象实例就不会被销毁,内存也不会释放。
php 的 unset() 只减少引用计数(refcount),并不立即触发垃圾回收;只要对象仍有其他有效引用(如静态属性),对象实例就不会被销毁,内存也不会释放。
在 PHP 中,变量并非直接“存储”对象,而是持有对对象的引用(更准确地说,是 zend_object_handle 的句柄)。当执行 $foo = new Foo 时,$foo 成为指向该对象的第一个引用;而在 Foo::__construct() 中将 self::$app = $this 赋值后,静态属性 $app 又创建了第二个独立引用——此时该对象的内部引用计数(refcount)变为 2。
调用 unset($foo) 并不会销毁对象本身,而只是将 $foo 变量从符号表中移除,并将对象的 refcount 减 1(变为 1)。由于 Foo::$app 仍持有有效引用,PHP 的垃圾回收器(GC)判定该对象“仍被使用”,因此对象实例及其内存持续存在。这也解释了为何 var_dump(Foo::$app) 仍能输出完整的 object(Foo)#1 ——它和原 $foo 指向的是同一块内存中的同一个对象实例,而非副本。
可通过 debug_zval_dump() 验证 refcount 行为:
<?php
class Foo {
public static $app;
public function __construct() {
self::$app = $this;
}
}
$foo = new Foo;
echo "对象是否相同?", var_export($foo === Foo::$app, true), "\n"; // true
// 查看 $app 当前 refcount(注意:debug_zval_dump 本身会临时增加 refcount)
debug_zval_dump(Foo::$app); // 输出类似:object(Foo)#1 (0) refcount=2
$foo = null; // 或 unset($foo),效果相同:refcount 降为 1
debug_zval_dump(Foo::$app); // refcount=1(实际值可能因 debug_zval_dump 干扰略有浮动,但趋势明确)⚠️ 注意事项:
立即学习“PHP免费学习笔记(深入)”;
-
===比较可确认两个变量是否引用同一对象(而非浅拷贝或新实例); -
debug_zval_dump()仅用于调试,其自身会临时增加 refcount,生产环境请勿使用; - 真正释放对象需所有引用全部解除,包括静态属性、闭包绑定、全局数组等隐式引用;
- 若需强制解耦,可显式置空静态引用:
Foo::$app = null;,再配合unset($foo),此时 refcount 归零,对象将在下一次 GC 周期被回收。
总结:PHP 的内存管理基于引用计数 + 循环引用 GC。unset() 是“减引用”操作,不是“删对象”操作。理解 refcount 机制,才能正确设计单例、容器、事件监听器等易产生长生命周期引用的模式。



















