__destruct() 在对象被垃圾回收判定为不可达时触发,非 unset() 立即执行,也非仅脚本结束才运行;其时机由引用计数归零与 GC 周期共同决定,可能延迟或不执行。

__destruct() 不是“一 unset 就执行”,也不是“脚本结束才统一跑”——它取决于 PHP 垃圾回收(GC)是否判定对象已不可达。你写的清理逻辑,可能延迟几毫秒,也可能根本没机会运行。
__destruct 什么时候真正被调用
触发时机不是由你“按下回车”决定的,而是由 PHP 的引用计数 + GC 周期共同判断:
- 脚本正常结束前,所有存活对象按“后创建、先销毁”顺序调用
__destruct()(类似栈) -
unset($obj)后,仅当该对象引用计数归零(无其他变量、数组元素、属性指向它),GC 下次扫描时才可能调用 - 函数内创建的对象,在函数返回后作用域消失,若无外部引用,会触发析构
- PHP 7.4+ 能识别并清理循环引用(如 A→B 且 B→A),旧版本可能永久泄漏、
__destruct()不执行 -
exit()或die()会触发析构;但进程被 kill -9 或发生 fatal error(如内存耗尽、未定义函数调用),则__destruct()可能跳过
为什么 unset($obj) 后 __destruct 没反应
常见错觉:以为 unset() = 立刻销毁 = 立刻调用 __destruct()。实际不是:
-
unset($obj)只是减少引用计数,不等于对象立刻不可达 - 如果对象还被
$arr['key'] = $obj、$other->ref = $obj或闭包 use 引用,就不会触发 - PHP GC 不是实时运行的,尤其在小脚本中,可能等到脚本快结束才扫一次
- 可通过
gc_collect_cycles()主动触发一轮回收(仅限 PHP 7.4+ 且启用了 GC),但不保证立即调用__destruct()
__destruct 中不能做的事
看似自由的清理入口,实则限制极多,踩坑即 fatal error:
立即学习“PHP免费学习笔记(深入)”;
- 禁止抛出异常:
throw new Exception()在__destruct()中会导致Fatal error: Uncaught Exception,无法捕获 - 禁止调用
exit()、die()或header()—— 输出已关闭或缓冲区已冲刷,行为不可控 - 避免依赖全局状态:此时
$_SERVER、$_SESSION可能已被重置或销毁 - 不要假设其他对象还活着:多个对象析构顺序不确定,
$this->db->close()安全,但$this->logger->log()若 logger 对象已析构,就会报错 - 不能带参数、不能有返回值,声明必须是
public function __destruct()
资源清理别只靠 __destruct
它只是兜底机制,不是主力方案。真实项目里容易忽略的关键点:
- 数据库连接、文件句柄这类有限资源,应主动
close()或disconnect(),而不是等__destruct() -
__destruct()适合做“尽力而为”的收尾:比如写日志、释放非关键缓存、标记状态,但不能承担核心事务一致性 - 测试时别只看脚本结尾输出——加
sleep(1)或gc_collect_cycles()再观察,否则容易误判触发逻辑 - PHP-FPM 模式下,请求结束 ≠ 脚本结束,对象可能复用(如 Swoole 长生命周期场景),
__destruct()触发时机更难预测



















