unset销毁变量名与值的绑定,不等于清空内容;它使变量失效,isset返回false,而赋值空数组或array_filter后变量仍存在且isset为true。

unset 不等于“清空”,它销毁的是变量名与值的绑定关系
很多人以为 unset($arr) 是把数组内容变为空数组 [],其实完全不是:它直接让 $arr 这个变量名失效,后续再访问会报 Notice: Undefined variable。而“去空值”(比如用 array_filter($arr) 或手动遍历 unset($arr[$key]))只是删掉某些元素,变量本身仍存在、仍可继续赋值。
-
unset($arr)后:isset($arr)返回false,$arr不再是有效变量 -
$arr = []或$arr = array_filter($arr)后:isset($arr)仍为true,变量名还在,只是内容变了 - 对引用变量要特别小心:
unset($b)不影响它引用的原值,除非所有引用都被unset了
unset 什么时候真正释放内存?别信“一调就 free”
PHP 的 unset() 并不保证立即归还内存给操作系统,它只断开符号绑定;内存是否回收,取决于底层 zval 的引用计数和 GC 触发时机。
- 小变量(如
$str = "hello",值 unset() 后 refcount 降为 0,内存通常立即复用,但未必交还 OS - 大变量(如含万级元素的数组、MB 级字符串):只有当它的 refcount 真正归零(无任何变量、属性、闭包引用它),且值大小 ≥256 字节时,PHP 才会触发底层
efree释放该块内存 - 对象循环引用(A→B→A):即使所有外部变量都
unset了,refcount 也不归零,必须等 GC 周期扫描后才能清理
想“去空值”又不想留冗余内存?别只靠 unset 单打独斗
常见场景:从数据库取大批量数据,过滤掉 null/empty 后处理,最后想轻装上阵——这时误用 unset() 可能适得其反。
- 错误做法:
foreach ($data as $k => $v) { if (empty($v)) unset($data[$k]); }→ 数组内部结构碎片化,内存未必减少,甚至因保留哈希表槽位而更耗内存 - 推荐做法:用
$data = array_filter($data, 'strlen')或重建数组$clean = []; foreach (...) if (...) $clean[] = $v;→ 得到紧凑新数组,旧数组 refcount 归零后更易被 GC 回收 - 极端情况(如导出前临时缓存百万行):显式
unset($original_data); $original_data = null;双保险,加速 refcount 清零
函数内 unset 全局变量?小心作用域陷阱
unset() 在函数里默认只作用于局部作用域——哪怕你传入的是全局变量名,也不会影响外部。
立即学习“PHP免费学习笔记(深入)”;
- 错例:
function clear() { unset($GLOBALS['cache']); }→ 实际删的是局部变量$GLOBALS的副本,外部$cache毫发无损 - 正确写法:
function clear() { global $cache; unset($cache); }或直接unset($GLOBALS['cache'])(注意:后者生效,因为$GLOBALS是超全局,修改其元素会影响全局) - 更安全的做法:统一用
$cache = null;替代unset(),语义清晰,且避免作用域混淆
unset,而是看还有没有其他地方悄悄持有着那个 zval 的引用——包括对象属性、静态变量、闭包 use、甚至 OPcache 中的常量表。调试时别只盯 memory_get_usage(),配合 xdebug_debug_zval() 查 refcount,才看得清真相。











