直接读取几百MB JSON文件时file_get_contents+json_decode会因内存耗尽崩溃,因其需全量加载文件至内存;应改用SplFileObject逐行处理或jsonstream等流式解析器。

为什么 file_get_contents + json_decode 会挂掉
直接读取几百 MB 的 JSON 文件时,file_get_contents() 会把整个文件塞进内存,PHP 进程大概率触发 Fatal error: Allowed memory size exhausted。这不是 JSON 解析的问题,是内存分配策略导致的——哪怕你只想要其中某个字段,PHP 也得先把全部字符串加载完才交给 json_decode()。
- 别指望改
memory_limit治本:设成 2G 可能暂时跑通,但服务器资源浪费、并发一上来就崩 -
json_decode()本身不支持流式解析,它必须拿到完整字符串才能工作 - 常见误区:以为加了
JSON_BIGINT_AS_STRING或JSON_INVALID_UTF8_IGNORE就能绕过内存问题——不能,这些只是解码选项,不影响读取阶段
用 SplFileObject + 手动拼接做轻量流读取
当 JSON 文件结构简单(比如顶层是数组,每行一个对象),可用 SplFileObject 分块读、边读边处理,避开大内存占用。适用于日志类 JSON Lines(.jsonl)或已知格式的批量数据。
- 先确认文件编码:用
mb_detect_encoding(file_get_contents($path, false, null, 0, 4))看前几个字节,避免 BOM 导致json_decode()返回null - 逐行读取并清理:
$line = trim($file->fgets(), "\xEF\xBB\xBF\r\n"),特别注意 UTF-8 BOM(\xEF\xBB\xBF) - 每行单独
json_decode($line, true),失败时立刻用json_last_error_msg()定位,不用等全文件扫完 - 别在循环里累积数组:处理完一行就入库或写入临时文件,否则还是吃内存
真正的大文件必须用流式 JSON 解析器
如果 JSON 是单个巨型对象(比如导出的数据库快照),SplFileObject 也没法拆——这时候得换库。原生 PHP 不支持,但 ext-json 扩展配合第三方流式解析器可行。
- 推荐
jsonstream(Composer 包):它基于fopen()+fgets(),按 token 解析,内存占用恒定在几 MB 内 - 安装:
composer require wyrihaximus/json-stream,然后用JsonStream::fromFile($path)获取可迭代对象 - 关键限制:它只支持 JSON 数组作为根节点(
[{...}, {...}]),不支持单个对象({...});若源文件是对象,得先用脚本包裹一层[]或改用jsonreader - XAMPP 环境下需确认 PHP 启用了
cURL和mbstring:流式解析器依赖它们做编码检测和分块缓冲
Apache + PHP 配置要同步调优
XAMPP 默认配置对大 JSON 处理很保守,光改 PHP 代码没用,Apache 和 PHP.ini 必须协同调整。
立即学习“PHP免费学习笔记(深入)”;
- Apache 端:在
httpd.conf里确认LimitRequestBody没被设成小值(默认是 0,即不限;但如果手动加过LimitRequestBody 1048576就得删掉) - PHP 端:修改
php.ini中的max_execution_time = 300(至少 5 分钟),post_max_size = 200M,upload_max_filesize = 200M - 重启服务后验证:
phpinfo()页面搜 “Loaded Configuration File” 确认改的是 XAMPP 自带的php.ini,不是系统级那个 - 别信
ini_set():运行时改memory_limit对file_get_contents()无效,它在脚本启动时就锁死了内存上限
真正卡住的点往往不在 JSON 解析函数本身,而在文件读取路径和 Apache 的请求体限制。先跑通一个 50MB 的 JSON Lines 文件,再碰 500MB 的单对象——顺序错了,调试成本翻倍。



















