You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS循环读取大量JSON文件时堆内存耗尽问题排查

Node.js堆内存耗尽问题:处理大量JSON文件的原因与优化方案

为什么会出现JavaScript heap out of memory错误?

你的代码里最核心的问题是用require()来读取JSON数据文件,而require的设计初衷是加载Node.js模块(包括JSON模块),它会自动缓存所有加载过的模块——也就是说,你读取的每一个6MB JSON文件,都会被永久存在内存的缓存里,不会被垃圾回收(GC)清除。

220个6MB的文件总大小是1320MB,再加上处理过程中产生的对象、V8引擎本身的内存开销,很容易就超过了Node.js默认的堆内存限制(通常在1.4GB左右),这就是为什么运行一段时间后会触发内存不足的报错。

另外,虽然你提到处理后的数据会快速提交到服务,但如果处理逻辑中不小心保留了对records或record对象的全局引用(比如存在某个不会被清空的数组、对象里),也会加剧内存泄漏,但最主要的元凶还是require的缓存机制。

当前的JSON读取方式正确吗?

不正确。require不适合用来读取大量一次性使用的数据文件:

  • 它的缓存机制会导致内存无法释放,对于批量处理场景非常不友好;
  • 同步读取的方式(readdirSync+require)会阻塞Event Loop,影响程序的响应性(虽然你的场景是批量处理,可能暂时不关心,但内存问题是硬伤)。

解决方法

1. 改用fs读取+手动JSON.parse(替代require)

放弃require,改用fs.readFileSync(同步场景)或fs.readFile(异步场景)读取文件内容,再手动解析JSON。这样读取的内容不会被缓存,处理完后如果没有引用,就会被GC回收:

const fs = require('fs');
const path = require('path');

const dirPath = '/500gb/json_files/gnip_30_p2';
let fileList = fs.readdirSync(dirPath);
fileList = fileList.reverse();

let totalErrors = 0;
let totalFiles = 0;

for (let file of fileList) {
  totalFiles++;
  const filePath = path.join(dirPath, file);
  // 读取文件内容并解析JSON
  const fileContent = fs.readFileSync(filePath, 'utf8');
  const records = JSON.parse(fileContent);
  
  const results = records.results;
  if (results) {
    for (let record of results) {
      // .. 你的处理逻辑 ..
    }
    // 手动解除引用,帮助GC回收(可选,V8通常会自动处理,但显式做更稳妥)
    records = null;
    results = null;
  }
}

2. 用流式解析进一步降低内存占用(推荐)

如果单个JSON文件结构支持(比如是数组格式的JSON),可以用流式解析库(比如JSONStream),不需要把整个6MB的JSON加载到内存,而是逐段读取解析,内存占用会大幅降低:

首先安装依赖:

npm install jsonstream

然后修改代码:

const fs = require('fs');
const path = require('path');
const JSONStream = require('JSONStream');

const dirPath = '/500gb/json_files/gnip_30_p2';
let fileList = fs.readdirSync(dirPath);
fileList = fileList.reverse();

let totalErrors = 0;
let totalFiles = 0;

// 用异步循环避免阻塞Event Loop
async function processFiles() {
  for (let file of fileList) {
    totalFiles++;
    const filePath = path.join(dirPath, file);
    
    await new Promise((resolve, reject) => {
      fs.createReadStream(filePath, 'utf8')
        .pipe(JSONStream.parse('results.*')) // 解析results数组里的每一项
        .on('data', (record) => {
          // .. 你的处理逻辑 ..
        })
        .on('error', (err) => {
          totalErrors++;
          console.error(`处理文件${file}出错:`, err);
          reject(err);
        })
        .on('end', resolve);
    });
  }
}

processFiles().then(() => {
  console.log(`所有文件处理完成,总文件数: ${totalFiles},错误数: ${totalErrors}`);
}).catch((err) => {
  console.error('处理流程出错:', err);
});

3. 临时方案:增加Node.js堆内存限制

如果你暂时不想修改代码,可以通过启动参数增加堆内存,比如设置为2GB:

node --max-old-space-size=2048 your-script.js

但这只是治标不治本的方法,随着文件数量增加,还是会遇到内存问题,优先推荐前两种方法。

内容的提问来源于stack exchange,提问作者Suhail Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 06:53:33