unset() 并非万能,但不用更糟:变量超作用域不立即释放内存,尤其大数组、对象或资源;循环中需显式 unset() 临时变量;array_splice() 比 array_slice() 更省内存;对象属性应延迟初始化;字符串拼接宜用数组收集后 implode;隐式引用链才是内存泄漏主因。

unset() 不是万能的,但不用它更糟
PHP 的变量在超出作用域后不会立刻释放内存,尤其是大数组、对象或资源句柄。不显式 unset(),可能让本该回收的内存一直挂着——特别是循环中反复构建大变量时。
常见错误现象:memory_get_usage() 持续上涨,脚本跑完也没回落;用 gc_collect_cycles() 强制回收也无效,因为引用还活着。
- 只对确定不再使用的变量调用
unset(),比如循环末尾的临时$data、解析后的原始 JSON 字符串$raw_json - 不要对函数参数或全局变量盲目
unset(),PHP 7.4+ 对局部变量的自动回收已很高效,乱删反而干扰优化 -
unset($arr)只断开变量名到 zval 的绑定,若$arr被其他变量引用(如$copy = $arr),内存不会释放
用 array_splice() 替代 array_slice() 处理大数组
读取大数组某一段时,array_slice() 总是复制一份新数组,哪怕你只要前 10 行;而 array_splice() 在原数组上截断并返回,配合 unset() 可避免双份内存占用。
使用场景:日志解析、CSV 批量导入、分页读取数据库结果集。
立即学习“PHP免费学习笔记(深入)”;
-
array_slice($big_arr, 0, 10)→ 新建一个 10 元素数组,原$big_arr还在内存里 -
$chunk = array_splice($big_arr, 0, 10)→$big_arr被原地缩短,内存立即松动 - 注意:如果后续还要用完整数组,别用
array_splice();它会修改原数组,不是“只读”操作
对象属性尽量延迟初始化,别堆在 __construct() 里
一个类有 5 个属性,其中只有 2 个在 90% 的请求里会被用到,但全在 __construct() 里 new 出来——等于每次实例化都扛着整套开销。
性能影响:对象创建变慢 + 内存常驻上升,尤其在 Laravel 等框架中被容器频繁解析时更明显。
- 把耗资源的属性(如
$this->cacheClient、$this->pdfGenerator)移到 getter 方法里首次访问时再初始化 - 用
isset($this->prop)或property_exists($this, 'prop')判断是否已建,避免重复 new - 慎用
__get()做懒加载,魔术方法有额外开销;普通 getter 更可控
字符串拼接别用 .= 循环追加超长内容
PHP 字符串是 copy-on-write,$s .= $part 在每次执行时都可能触发内存重分配和拷贝。1000 次追加,最坏情况产生 O(n²) 内存复制。
错误现象:生成大 HTML 或导出 CSV 时,内存峰值远超最终字符串长度的 2–3 倍。
- 改用
array_push($parts, $part)收集片段,最后implode('', $parts) - 如果必须流式构建(比如边查库边写),考虑直接
fwrite()到临时文件或php://temp流,绕过内存缓冲 -
sprintf()或strtr()替代多层.拼接,减少中间字符串对象数量
真正吃内存的往往不是单个大变量,而是隐式引用链:闭包捕获了整个上下文、PDOStatement 持有未 fetch 完的结果集、静态属性缓存了不该缓存的对象。查内存得先看 xdebug_debug_zval() 或 debug_zval_dump(),而不是急着 unset()。











