PHP-FPM子进程内存不会自动释放,ThinkPHP长循环需手动触发gc_collect_cycles(),并配合pm.max_requests限制、内存限制调优及静态属性泄漏防控。
PHP-FPM子进程内存不会自动释放,ThinkPHP长循环必须手动触发GC
PHP-FPM每个worker进程处理完请求后,
、
等超全局变量会被重置,但PHP的引用计数和循环引用对象(比如模型关联、闭包、静态属性缓存)并不会被立即回收。ThinkPHP 5.x/6.x中大量使用
单例、
链式查询、
集合等,若在命令行或长耗时接口中反复执行数据库操作,内存会持续累积——哪怕没报错,
也可能显示增长几十MB。
实操建议:
立即学习
“
PHP免费学习笔记(深入)
”;
在循环体末尾显式调用
,尤其在
/
中每处理100条数据后触发一次
避免在循环内反复调用
或
而不释放结果集;改用
流式读取大表
清空ThinkPHP内部缓存:对
实例调用
,或直接
(TP6)
禁用
(CLI模式下OPcache可能干扰GC)
ThinkPHP模型静态属性泄漏是高频内存陷阱
TP5.1+中
类的
、
、
等都是静态属性,一旦某次查询触发了关联预加载(如
),相关模型元信息就会常驻内存。更隐蔽的是
返回的对象会持有
、
、
等大数组,而这些对象若被闭包捕获、存入静态数组或全局缓存,就彻底无法被GC识别。
实操建议:
立即学习
“
PHP免费学习笔记(深入)
”;
禁止在循环中用
批量创建模型实例;改用
直写SQL
主动切断模型引用:
,再调用
检查自定义基类是否在
里注册了
或
,这类监听器常带闭包,易造成循环引用
用
检查关键变量的refcount,确认是否意外被静态容器持有
配置
php
.ini与FPM pool参数比优化代码更立竿见影
很多团队花几天调
分页逻辑,却忽略FPM子进程本身已分配256MB内存上限——当
时,一个worker可能扛上千次请求,中间任何一次泄漏都会累积。而
设得过高反而掩盖问题,让GC失效更久。
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
实操建议:
立即学习
“
PHP免费学习笔记(深入)
”;
FPM pool中强制设置
(不是0),让worker定期重启释放全部内存
中设
(非256M),配合
和
捕获
关闭
和
,减少OPcache内存占用
禁用
等非必要扩展,它们的持久化连接池也占内存
用cli模式复现并验证内存问题最可靠
Web请求受Nginx超时、浏览器中断影响,内存泄漏现象不稳定。而
这类命令行脚本能稳定复现——只要
在第10次循环后比第1次高30%以上,基本可判定存在泄漏。
实操建议:
立即学习
“
PHP免费学习笔记(深入)
”;
写最小复现脚本:只包含
循环 +
打印,排除模板、日志等干扰
用
快速触发溢出,定位哪一行首次突破阈值
对比开启
(默认已开)和
后的差异,确认是否真由GC机制导致
TP6中启用
时,
会缓存全部SQL,记得在cli测试前临时关闭
真正难处理的不是单次溢出,而是那些每次只涨几KB、跑几百次才崩的渐进式泄漏——它往往藏在第三方SDK、自定义事件监听或ORM关联预加载的缝隙里。盯住
返回的
和
字段,比看错误日志更早发现问题。
$_SERVER$_GETContainerDb::name()Collectionmemory_get_usage(true)gc_collect_cycles()forwhilenew Model()Db::table()->select()Db::cursor()think\Cacheclear()Cache::clear()opcache.enable_cli=1think\Model$connection$schema$relationwith('user')Model::get()$data$origin$relationModel::create($data)Db::insertAll()unset($model); $model = null;gc_collect_cycles()__constructApp::after()Event::listen()xdebug_debug_zval()Db::chunk()pm.max_requests = 0memory_limitpm.max_requests = 200~500php.inimemory_limit = 128Mlog_errors = Onerror_log = /var/log/php-fpm-memory.logFatal error: Allowed memory size of xxx bytes exhaustedopcache.save_comments = 0opcache.load_comments = 0extension=memcached.sophp think test:leakmemory_get_peak_usage()Db::name('user')->select()memory_get_usage()php -d memory_limit=64M your_script.phpzend.enable_gc=1gc_disable()app_debug = trueLog::record()gc_status()runscollected