oom本质是单个请求内存失控被并发放大,需用memory_get_usage()实时监控、yield降内存、禁用pdo缓冲、固定memory_limit、主动触发gc_collect_cycles。

PHP高并发场景下出现OOM(Out of Memory),本质不是“并发太高”,而是单个请求内存失控后被放大——比如一个接口在低流量时用128MB,到高并发时每个请求都卡在256MB不释放,瞬间压垮服务器。避免的关键是:**不让单个请求失控,再辅以资源隔离与监控兜底**。
怎么用 memory_get_usage() 实时盯住内存水位
别等报错才行动。在关键入口、循环体前后、大查询之后插入检测点,把内存当CPU使用率一样监控:
-
memory_get_usage(true)返回真实分配的内存块大小(含内存对齐开销),比默认值更准 - 建议设阈值告警,比如超过100MB就记录日志:
if (memory_get_usage() > 100 * 1024 * 1024) error_log("High memory usage at " . debug_backtrace()[0]['function']); - 注意:CLI模式下
memory_get_peak_usage()比Web更可靠,因Web服务器可能复用进程导致历史峰值残留
生成器 yield 不是语法糖,是内存安全阀
处理数据库结果集、大文件、API流式响应时,yield 能把O(n)内存降到O(1)。但很多人误以为“用了yield就万事大吉”,其实有坑:
- 必须配合
foreach消费,若转成array_values(iterator_to_array($gen))会立刻崩 - 数据库PDO需禁用缓冲:
$pdo->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);,否则fetch()仍会把整张表载入内存 - 示例:分页查10万条订单,不要
$orders = $pdo->query("SELECT * FROM orders")->fetchAll();改用生成器逐批拉取
ini_set('memory_limit', ...) 在高并发下反而加速OOM
动态调高内存限制看似救急,但在高并发中极易引发雪崩:
立即学习“PHP免费学习笔记(深入)”;
- 每个请求都执行
ini_set('memory_limit', '512M'),等于给每条线程发了512MB“信用额度”,而系统总内存没变 - Apache prefork模式下,子进程常驻,
ini_set修改会持续生效,后续请求可能继承异常高的限制 - 真正该做的是:在
php.ini里按环境分级配置(如CLI用-1,Web用256M),并在脚本开头用ini_set('memory_limit', '256M')做硬性封顶,防止上游配置被绕过
最易被忽略的一点:PHP的垃圾回收(GC)在高并发下并不积极——它默认只在内存池满或脚本结束时触发。如果代码存在循环引用(比如对象A持有了闭包,闭包又引用了A),unset()根本不管用。这时得主动调用gc_collect_cycles(),尤其在长循环末尾或大对象销毁后。











