NodeJS使用JSONStream处理大JSON时出现OOM问题求助
Node.js流式处理大JSON时出现内存溢出问题
我尝试用NodeJS的JSONStream库将94MB的JSON通过HTTP请求流式传输到本地文件,但应用出现了out-of-memory错误,哪怕已经用node --max-old-space-size=256 .\main.js设置了256MB内存上限。流式处理的核心不就是用小数据块而非加载整个JSON到内存吗?是不是我的代码哪里写错了?
源代码
const http = require('http'); const JSONStream = require('JSONStream'); const https = require('https'); const { pipeline, Transform } = require('stream'); const fs = require('fs'); const mb100 = 'https://raw.githubusercontent.com/seductiveapps/largeJSON/master/100mb.json'; // 补充缺失的isHttps函数 function isHttps(url) { return url.startsWith('https://'); } new Promise((res, rej)=> { const url = mb100; const protocol = isHttps(url) ? https : http; const parse = JSONStream.parse('*'); const write = fs.createWriteStream('file.json'); const str = new Transform({ objectMode:true, transform: function (chunk, encoding, callback) { // some computations, just an example: callback(null, JSON.stringify(chunk)); } }) protocol.get(url, (networkStream) => { pipeline(networkStream, parse, str, write, (err) => { console.log(err ?? 'finish'); }); }) })
问题分析
你遇到内存溢出的核心原因是多余的JSON解析与序列化步骤,以及该步骤带来的内存占用:
- 流式处理的核心确实是逐块处理数据,但
JSONStream.parse('*')会将JSON数组的单个完整元素加载到内存中(而非拆分元素本身),如果原JSON里的单个元素体积较大,就会快速占用内存。 - 你后续又将解析出的对象重新
JSON.stringify(),这个过程会再次占用内存生成字符串,相当于同一数据在内存中存在对象和字符串两种形式,进一步加剧内存消耗。 - 另外,代码中缺失
isHttps函数会导致运行报错,但这不是内存溢出的直接原因。
解决方案
根据你的实际需求,分两种情况处理:
1. 仅需下载保存JSON文件(无需修改内容)
如果只是要把远程JSON文件流式保存到本地,完全不需要JSON解析和转换步骤,直接将网络流对接文件流即可,内存占用极低:
const https = require('https'); const { pipeline } = require('stream'); const fs = require('fs'); const mb100 = 'https://raw.githubusercontent.com/seductiveapps/largeJSON/master/100mb.json'; pipeline( https.get(mb100), fs.createWriteStream('file.json'), (err) => { console.log(err ?? 'finish'); } );
2. 需要对JSON元素做处理
如果必须处理JSON中的每个元素,优化方向如下:
- 确认JSON结构:确保
JSONStream.parse('*')匹配的是顶层数组元素,如果原JSON不是数组,需调整解析路径(比如JSONStream.parse('data.*')针对嵌套数组)。 - 优化处理逻辑:避免不必要的序列化,若处理后仍需输出JSON,尽量直接修改解析后的对象,减少内存中重复存储的数据。
- 控制背压:如果处理逻辑较慢,可在Transform流中通过
callback的延迟调用控制数据流入速度,避免数据堆积:const str = new Transform({ objectMode:true, transform: function (chunk, encoding, callback) { // 模拟耗时处理 setTimeout(() => { // 示例处理逻辑 chunk.processed = true; callback(null, JSON.stringify(chunk) + '\n'); }, 10); } })
内容的提问来源于stack exchange,提问作者Romas Bitinas
相关产品推荐
相关产品推荐

