在while循环中使用Laravel Cache门面引发内存泄漏的排查
解决Laravel长期运行命令中Cache::get()导致的内存泄漏问题
问题背景
基于PHP 8.3、Redis 7的Laravel 11应用,在继承自长期运行自定义Worker结构的Artisan命令中,while循环调用Cache::get()时内存持续攀升,移除该调用则恢复正常。应用使用了Laravel Telescope和Octane,已尝试垃圾回收但无效。
核心原因
- Telescope日志积累:Telescope会记录每一次Cache操作,长期运行的进程中这些日志条目会持续占用内存,无法被及时回收。
- Octane常驻进程的状态残留:Octane环境下进程常驻,Cache相关的框架实例可能积累状态或对象引用,导致内存无法释放。
- 循环引用无法被GC回收:Cache操作过程中可能产生循环引用,PHP默认GC无法完全清理这些引用。
解决方案
禁用Telescope对当前命令的监控
两种方式任选其一:- 在命令的
handle方法开头直接停止Telescope记录:\Laravel\Telescope\Telescope::stopRecording(); - 在
config/telescope.php的ignore_commands数组中添加该命令,永久跳过监控:'ignore_commands' => [ 'app:memory-loop-test', // 其他需要忽略的命令 ],
- 在命令的
改用原生Redis客户端
绕开Laravel Cache门面的封装,直接调用Redis原生客户端,减少框架层的对象生成和状态积累:// 替换原有的Cache::get('name') $redis = app('redis')->connection(); $get = $redis->get('name');定期重置Cache实例
在循环内手动清除容器中的Cache实例,重新获取新实例,避免状态残留:while (true) { dump(memory_get_usage() / (1024 * 1024)); // 重置Cache实例 app()->forgetInstance('cache'); $cache = app('cache'); $get = $cache->get('name'); }优化变量释放与垃圾回收
结合变量销毁和强制GC,确保内存被及时回收:while (true) { dump(memory_get_usage() / (1024 * 1024)); $get = Cache::get('name'); // 销毁变量,解除引用 unset($get); // 强制触发垃圾回收 gc_collect_cycles(); // 可选:短暂休眠,降低CPU占用 Sleep::milliseconds(100); }设置Worker自动重启机制
针对长期运行的Worker,设置循环次数阈值,达到后自动退出,由进程管理器(如Supervisor)重启进程,从根源上避免内存积累:$loopCount = 0; $maxLoops = 1000; // 可根据实际情况调整 while (true) { $loopCount++; dump(memory_get_usage() / (1024 * 1024)); $get = Cache::get('name'); if ($loopCount >= $maxLoops) { exit; } }
内容的提问来源于stack exchange,提问作者Ryan H
相关产品推荐
相关产品推荐

