PHP 8.0+ 迁移后内存不释放主因是 ini_set('memory_limit', ...) 优先级高于环境变量和 php.ini,早期执行会锁死限制(如128M),覆盖Docker中配置的2G;需全局搜索删除硬编码调用,并用php.ini或CLI参数统一控制。

PHP 8.0 迁移后内存不释放,八成不是 GC 失效,而是旧写法在新生命周期下“卡住”了释放时机——比如静态变量跨请求滞留、WeakMap 未用、或 ini_set('memory_limit', ...) 意外锁死限制。
检查是否被 ini_set('memory_limit', ...) 锁死了上限
PHP 8.0+ 中,ini_set('memory_limit', ...) 的优先级高于 php.ini 和环境变量,一旦在测试启动早期(如 TestCase.php 或 bootstrap)执行,就会覆盖你 Docker 里设的 2G,实际只剩 128M。
- 在入口或测试 setUp() 里加
echo ini_get('memory_limit');验证真实值 - 全局搜索项目(含 vendor):
grep -r "ini_set.*memory_limit" --include="*.php" . - 删掉所有硬编码调用,改用 php.ini 或 CLI 启动参数控制
确认对象是否被静态引用或闭包捕获滞留
PHP 请求结束时会清空局部变量和超全局数组,但 static 变量、类静态属性、或闭包 use 捕获的对象不会自动释放——尤其在常驻进程(如 Swoole Worker、ReactPHP)中会持续累积。
- 避免
static $cache = [];存对象,改用WeakMap: -
$cache = new WeakMap(); $cache[$obj] = $metadata;—— 对象销毁后条目自动消失 - 闭包中若需外部变量,显式
use ($id)而非use ($obj),防止无意持引用
Excel / 大文件处理后没真正释放资源
TP8.0 + PhpSpreadsheet 场景下,IOFactory::load() 后直接 unset($spreadsheet) 不够——样式、公式、计算引擎等底层资源仍挂在 Zend 内存池里。
立即学习“PHP免费学习笔记(深入)”;
- 必须显式关闭:$spreadsheet->disconnectWorksheets();
- 再调用
unset($spreadsheet),最后补一句gc_collect_cycles(); - 更稳妥的做法是根本不用
load(),改用流式读取:createReader('Xlsx')->setReadDataOnly(true)->setLoadSheetsOnly(['Sheet1'])
别依赖 gc_collect_cycles() 解决设计问题
这个函数只是强制触发一次垃圾回收,对循环引用有效,但无法解决根本性滞留:比如 PDOStatement 未 closeCursor()、Redis 连接未 close()、或 SplFixedArray 占着大块连续内存不归还系统。
- 数据库查询后立即
$stmt->closeCursor() - 不再使用的资源对象(如
ZipArchive、XMLReader)调用其明确的close()或unload()方法 - 大数组处理完尽快
unset(),不要等作用域自然结束——尤其在长循环中
最易忽略的是:PHP 8.0 的 RSHUTDOWN 阶段,文件句柄、日志流、甚至某些扩展的内部缓存可能已被提前关闭,此时再试图写日志或释放资源会静默失败。释放动作要放在业务逻辑末尾、而非靠析构函数兜底。



















