应避免使用range(1, 10000000)因它会一次性分配并填充全部内存,导致OOM;改用自定义xrange Generator实现惰性迭代,内存恒定约1KB,适用于循环、分页、文件处理等场景。

range(1, 10000000) 直接崩,不是配置不够,是设计错了
range 本质是一次性 malloc + memset:它必须立刻分配完整数组内存,并逐个填值。哪怕你只用 foreach 遍历一次,PHP 也得先把一千万个整数全塞进内存——这不是“慢”,是根本没给机会优化。查 memory_get_usage(true) 就会发现,卡顿点就落在 range() 调用那一行,暴涨几百 MB。别急着调 memory_limit,先确认业务是否真需要这个数组。
- 如果只是循环计数或生成序列,直接改用
for循环,不存数组 - 如果要传给第三方函数(如
array_map),检查它是否接受Traversable;很多现代库已支持 Generator - 如果必须返回“类数组”结构,但下游只读不写、不随机访问,优先走 Generator 路线
用 xrange 替代 range:自己写一个内存恒定的迭代器
PHP 没内置xrange,但几行就能实现。它不返回数组,而返回一个 Generator,每次 foreach 只产出一个整数,内存占用几乎不变(约 1KB 左右)。
function xrange(int $start, int $end, int $step = 1): Generator {
if ($step > 0 && $start > $end) return;
if ($step < 0 && $start < $end) return;
for ($i = $start; $step > 0 ? $i <= $end : $i >= $end; $i += $step) {
yield $i;
}
}- 注意边界判断:$start 和 $end 顺序反了时,不进入循环,避免无限 yield
- 不要对
xrange调用count()或array_values(),它不是数组 -
foreach (xrange(1, 10000000) as $i)安全;但$arr = iterator_to_array(xrange(1, 10000000))会立刻 OOM
数据库/文件场景下,别用 range 做分页偏移
常见错误:用range($offset, $offset + $limit - 1) 生成 ID 列表,再 WHERE id IN (...) 查询。这在 $offset 达到百万级时,range 先爆内存,SQL 还可能因 IN 列表过长失败。
- 真正该做的是游标分页:
WHERE id > $last_seen_id ORDER BY id LIMIT $limit - 如果必须基于数值范围查询(如导出某天所有订单号),直接用 SQL 的
BETWEEN,别把范围拉到 PHP 层生成 - 日志或 CSV 文件按行处理?用
fopen+fgets+yield,而不是file()全读再range索引取
Generator 调试时看不到内容,这是正常现象
var_dump(xrange(1, 1000000)) 只显示 object(Generator)#1,不是 bug,是 Generator 的惰性特性决定的。想看前 10 项?得手动消费:
foreach (xrange(1, 1000000) as $i) {
if ($i > 10) break;
echo "$i ";
}-
iterator_to_array($gen, false)能展开,但等于放弃优化,回到 OOM 原点 - 单元测试里验证 Generator 行为,用
yield from或ArrayIterator包一层做断言更安全 - CLI 脚本中,加一句
gc_collect_cycles()在长循环后,能防止 Generator 内部状态残留拖慢 GC
真正卡住的从来不是数据量,而是把流式问题硬套成静态数组的习惯。Generator 不是高级技巧,是处理大范围数值时的默认选项。



















