Node.js应用出现JavaScript堆内存溢出问题求解决方案
解决Node.js堆内存耗尽(Reached heap limit Allocation failed)问题的思路
你的代码引发内存溢出的核心原因是一次性加载了大量media文档到内存,加上未等待save操作完成导致Promise堆积,最终耗尽堆内存。以下是具体修复方案:
1. 分页查询,避免一次性加载全量数据
如果medias数据量很大,this.mediaModel.find()会把所有MongoDB文档加载到内存中,直接占用大量堆空间。改成分页分批处理,每次只加载固定数量的文档,处理完再加载下一批:
async updateLinks() { this.logger.log('Cron Job: Updating Urls .....') const batchSize = 50; // 根据服务器内存调整批次大小 let skip = 0; let hasMore = true; while (hasMore) { // 分页查询 const medias = await this.mediaModel.find().skip(skip).limit(batchSize); if (medias.length === 0) { hasMore = false; break; } // 逐批处理 for (let media of medias) { const url = await this.testService.getPreSignedUrl(media.key); media.url = url; await media.save(); // 必须等待保存完成,避免Promise堆积 } skip += batchSize; // 可选:手动触发垃圾回收(需启动时加--expose-gc参数) global.gc?.(); } this.logger.log('Cron Job: Completed ') }
2. 等待save操作完成
原代码中media.save()没有加await,会导致大量未完成的保存Promise同时存在,每个Promise都会占用内存资源。加上await确保每个保存操作完成后再处理下一个文档,避免内存堆积。
3. 控制S3请求并发数(可选,兼顾效率与内存)
如果想提高处理效率,同时避免并发过高导致内存占用飙升,可以在批次内控制并发请求数:
// 在分页循环内替换原for循环 const concurrency = 10; // 控制同时发起10个S3请求和保存操作 for (let i = 0; i < medias.length; i += concurrency) { const subBatch = medias.slice(i, i + concurrency); await Promise.all(subBatch.map(async media => { const url = await this.testService.getPreSignedUrl(media.key); media.url = url; return media.save(); })); }
4. 临时应急:增加堆内存上限
如果需要快速临时解决,可以在启动Node.js应用时增加堆内存限制,比如设置为4GB:
node --max-old-space-size=4096 your-app.js
注意:这只是临时方案,无法根治数据量过大的问题,最终还是要通过代码优化解决。
5. 排查内存泄漏
如果优化后仍有问题,可以用Node.js调试工具排查内存泄漏:
- 启动应用时添加
--inspect参数 - 在Chrome浏览器中打开
chrome://inspect,连接到应用 - 使用Memory面板生成内存快照,查看是否有大量未被回收的
media实例或其他对象
内容的提问来源于stack exchange,提问作者Илья Никитин
相关产品推荐
相关产品推荐

