
php 的 unset() 只减少变量引用计数,并不立即销毁对象;只要存在其他有效引用(如静态属性),对象实例仍保留在内存中,这是由 php 的垃圾回收机制决定的。
php 的 unset() 只减少变量引用计数,并不立即销毁对象;只要存在其他有效引用(如静态属性),对象实例仍保留在内存中,这是由 php 的垃圾回收机制决定的。
在 PHP 中,unset($variable) 并非“删除对象”,而是解除该变量名对底层 zval(Zend Value)的绑定,并将其引用计数(refcount)减 1。只有当某个对象的 refcount 降为 0 时,PHP 的垃圾回收器(GC)才可能在后续周期中真正释放其内存。
以你提供的代码为例:
class Foo {
public static $app;
public function __construct() {
self::$app = $this; // $this 被赋值给静态属性,产生第二个强引用
}
}
$foo = new Foo; // 创建对象,refcount = 1(来自 $foo)
// 此时 self::$app = $this,refcount 自动增至 2
unset($foo); // 仅移除 $foo 绑定,refcount 减为 1 → 对象未被销毁
var_dump($foo); // Notice: Undefined variable → $foo 已不存在
var_dump(Foo::$app); // object(Foo)#1 → 对象依然存活!
关键点在于:$foo 和 Foo::$app 指向同一个对象实例(可通过 === 验证),而非副本。它们共享同一块内存地址,因此修改任一变量的属性都会反映在另一端。
你可以使用 debug_zval_dump() 直观观察 refcount 变化:
$foo = new Foo; echo "初始 refcount:\n"; debug_zval_dump(Foo::$app); // refcount=2($foo + static $app) $foo = 90; // 修改 $foo 类型 → 触发 Copy-on-Write,$foo 不再引用原对象 echo "\n修改 $foo 后:\n"; debug_zval_dump(Foo::$app); // refcount=1(仅剩 static $app)
⚠️ 注意事项:
- 静态属性属于类级别,生命周期贯穿整个请求,除非显式置为
null或脚本结束,否则不会自动解引用; - 若需强制释放对象,请手动清除所有引用:
Foo::$app = null;; - PHP 8.0+ 改进了循环引用 GC,但单向强引用(如本例)仍依赖 refcount 归零;
-
unset()不影响对象内部状态,也不触发__destruct()—— 析构函数仅在对象真正销毁时调用。
总结:unset() 是“去绑定”而非“销毁”。理解 PHP 的引用计数模型和对象生命周期,是避免内存泄漏与调试意外行为的关键。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











