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

Node.js使用Promise.all并行请求时遇JavaScript堆内存溢出求助

Node.js并行数据获取引发堆内存溢出问题排查与解决

问题背景

为提升程序速度,改用并行方式替代串行获取数据,核心代码如下:

function func() {
  var promises = [];
  for (let route of routes) {
    promises.push (calc(route, amount_wei));
  }
  Promise.all(promises).then(function(completedItems) {
    completedItems.forEach(function(val) {
      var result_amount = val;
      if (result_amount[5] > amount_start * 1) {
        console.log("Good Trade");
      }
    }
  });
}

仅循环10次(对应10个路由),却频繁触发JavaScript堆内存溢出,错误日志如下:

<--- Last few GCs --->

[23272:000001FDFC5C4DE0]   126463 ms: Mark-sweep (reduce) 9852.7 (10047.2) -> 9852.3 (10047.9) MB, 9776.7 / 0.0 ms  (average mu = 0.350, current mu = 0.000) allocation failure scavenge might not succeed
[23272:000001FDFC5C4DE0]   136795 ms: Mark-sweep (reduce) 9853.4 (10047.9) -> 9853.0 (10048.7) MB, 10314.7 / 0.0 ms  (average mu = 0.201, current mu = 0.002) allocation failure scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
 1: 00007FF6487D7C4F v8::internal::CodeObjectRegistry::~CodeObjectRegistry+114207
 2: 00007FF648765EC6 DSA_meth_get_flags+65542
 3: 00007FF648766D7D node::OnFatalError+301
 4: 00007FF64909B6CE v8::Isolate::ReportExternalAllocationLimitReached+94
 5: 00007FF649085CAD v8::SharedArrayBuffer::Externalize+781
 6: 00007FF648F2907C v8::internal::Heap::EphemeronKeyWriteBarrierFromCode+1468
 7: 00007FF648F26194 v8::internal::Heap::CollectGarbage+4244
 8: 00007FF648F23B10 v8::internal::Heap::AllocateExternalBackingStore+2000
 9: 00007FF648F48696 v8::internal::Factory::NewFillerObject+214
10: 00007FF648C7AB65 v8::internal::DateCache::Weekday+1797
11: 00007FF6491293C1 v8::internal::SetupIsolateDelegate::SetupHeap+494417
12: 00007FF6490EB013 v8::internal::SetupIsolateDelegate::SetupHeap+239523
13: 000001FD8014DF37

已尝试的解决方法:

  • 设置环境变量NODE_OPTIONS为--max-old-space-size=8192
  • 用命令node --max-old-space-size=8192 index.js启动程序

当前堆统计信息:

{
  total_heap_size: 55885824,
  total_heap_size_executable: 786432,
  total_physical_size: 55885824,
  total_available_size: 10505880728,
  used_heap_size: 29100784,
  heap_size_limit: 10536091648,
  malloced_memory: 794688,
  peak_malloced_memory: 4109184,
  does_zap_garbage: 0,
  number_of_native_contexts: 1,
  number_of_detached_contexts: 0
}

排查与解决方案

1. 定位内存泄漏根源:检查calc函数

从堆统计看,当前堆使用量远未达限制,但GC日志显示堆占用接近10GB,说明**calc函数内部可能存在内存泄漏或生成了超大数据对象**:

  • 检查calc是否在每次调用时创建大量未被回收的对象、缓存,或请求返回了远超预期的大体积数据
  • 确认calc是否存在循环引用,导致对象无法被GC回收
  • 临时将calc替换为返回固定值的模拟函数,验证是否还会触发内存溢出,以此排除并行逻辑本身的问题

2. 优化并行执行策略:控制并发量

即使只有10个请求,若每个calc都占用大量内存,同时执行仍可能压垮堆内存。可改为分批并行:

async function func() {
  const batchSize = 2; // 每次并行执行2个请求,可根据内存情况调整
  for (let i = 0; i < routes.length; i += batchSize) {
    const batch = routes.slice(i, i + batchSize);
    const results = await Promise.all(batch.map(route => calc(route, amount_wei)));
    results.forEach(val => {
      if (val[5] > amount_start * 1) {
        console.log("Good Trade");
      }
    });
    // 显式释放引用,帮助GC快速回收
    batch.length = 0;
    results.length = 0;
  }
}

3. 强制GC与内存监控

在关键节点手动触发GC(仅用于调试,生产环境不建议),同时监控内存变化:

const v8 = require('v8');

async function func() {
  // ... 分批执行代码 ...
  // 每批执行完后触发GC
  if (global.gc) {
    global.gc();
    console.log('GC executed');
    console.log(v8.getHeapStatistics());
  }
}
// 启动时需添加--expose-gc参数:node --expose-gc --max-old-space-size=8192 index.js

4. 排查外部资源泄漏

如果calc涉及数据库连接、文件流、网络请求等外部资源,需确认是否存在未正确关闭的资源:

  • 检查是否有未释放的数据库连接池、WebSocket连接
  • 确认请求是否设置了超时,避免长时间挂起导致资源堆积

5. 验证内存限制是否生效

有时--max-old-space-size参数可能未正确生效,可通过以下代码确认:

const v8 = require('v8');
console.log('Heap limit:', v8.getHeapStatistics().heap_size_limit / (1024 * 1024), 'MB');

若输出不是8192MB,说明参数未生效,需检查启动命令或环境变量是否正确设置(比如Windows环境下环境变量是否生效、是否有其他命令覆盖了参数)


内容的提问来源于stack exchange,提问作者Dani S

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 22:45:33