unset 的真实作用是断开变量名与 zval 的绑定,而非直接释放内存;是否回收取决于引用计数和垃圾回收器,memory_get_usage() 不变是因内存池复用,并非未释放。

不是立刻释放,但确实是释放内存的关键一步。
unset 的真实作用:断开绑定,不是清空内存
unset($var) 的核心行为是把变量名从符号表里删掉,让它和背后的 zval(PHP 内部值容器)“脱钩”。这步做完,变量就访问不了了,再读会报 Notice: Undefined variable。但它不等于把那块内存还给系统——是否回收、何时回收,取决于 zval 的引用计数和垃圾回收器(GC)。
- 如果这个 zval 当前只有 $var 一个引用(refcount = 1),unset 后 refcount 变 0,内存通常马上可回收;
- 如果还有其他变量或结构指向它(比如 $b = $var; 或被塞进静态数组、闭包 use 里),refcount > 0,unset 只是减一,内存还得留着;
- 对循环引用的对象(A→B→A),即使所有外部引用都 unset 了,refcount 也不归零,必须等 GC 周期检测并清理。
为什么 memory_get_usage() 没变化?
常见错觉是“没降就是没释放”。其实有三个原因:
- 你用的是 memory_get_usage(false)(默认),它返回的是 PHP 当前实际使用的字节数,但 Zend 内存管理器可能把刚释放的内存块保留在池里,供后续快速分配,所以数字不动;
- 真正向操作系统申请/归还的总量,得看 memory_get_usage(true);
- 脚本还在运行中,PHP 不一定会立刻把内存交还系统,而是复用——这是性能优化,不是 bug。
更有效的释放组合策略
单靠 unset 往往不够,尤其处理大数组、对象或资源时:
立即学习“PHP免费学习笔记(深入)”;
- 大数组或对象引用多时,优先用 $var = null:它保留变量名但切断数据引用,比 unset 更容易让 refcount 归零(尤其当 $var 是唯一对外出口时);
- 确认无其他引用后,加 gc_collect_cycles():手动触发 GC,对循环引用结构特别有效(PHP 5.3+ 支持);
- 资源型属性必须显式清理:比如 $obj->fileHandle = null 或 fclose($obj->fileHandle),否则文件句柄、数据库连接一直占着内存;
- 别碰超全局变量整体 unset:unset($_SESSION) 或 unset($_POST) 会破坏框架上下文,导致后续逻辑异常。
什么时候其实不用管?
多数情况下,你不需要手动干预:
- 函数内定义的变量,函数返回后自动清理,比手动 unset 更安全可靠;
- 循环里的临时变量(如 foreach ($items as $item)),每次迭代自然覆盖,无需每轮 unset;
- 频繁 unset + 重建小变量,反而增加符号表操作开销,得不偿失。



















