Node.js分块提取数据内存占用异常:为何与原文件大小一致?
分块提取数据仍占用全量内存的原因分析
问题背景
原文件大小2.24MB,仅需提取其中0.2MB的数据,通过分块处理试图降低内存占用,但内存测量结果显示占用量仍接近2.2MB,实际提取出的buffer大小仅0.19MB。
用户提供的代码
数据提取函数
function foo(url,sectionToWatchStart,sectionToWatchEnd ) { let isDataSectionFound = false; return new Promise((resolve, reject) => { let buffer= ""; https .get(url, function (response) { response.on("data", function (chunk) { if (chunk.includes(sectionToWatchStart)) isDataSectionFound = true; if (isDataSectionFound) { buffer += chunk; if (chunk.includes(sectionToWatchEnd)) { isDataSectionFound = false; } } }); response.on("end", function () { resolve(buffer); // the measure size of buffer variable is just 0.19MB }); }) .on("error", function (error) { console.error(error); reject(); }); }); }
内存测量工具函数
// Util Func to measure the size: const mesurMemory = async () => { const initialMemoryUsage = process.memoryUsage().heapUsed; const res = await foo(); const finalMemoryUsage = process.memoryUsage().heapUsed; const memoryUsageForCode = finalMemoryUsage - initialMemoryUsage; console.log(`Memory usage for code: ${memoryUsageForCode / (1024 * 1024)}MB`); };
核心原因
- Node.js HTTP响应默认加载全量数据:哪怕你只需要部分内容,
https.get会把整个2.24MB的响应数据全部接收并缓存到内存里,你的代码只是从中筛选出了目标片段,但全量数据已经被加载,内存自然降不下来。 - 字符串拼接的额外内存开销:JS里字符串是不可变的,每次
buffer += chunk都会生成新的字符串,旧的字符串不会立刻被垃圾回收,这会产生额外的内存碎片和占用。 - 内存测量时机不对:在
end事件触发时,全量响应的缓存还没被垃圾回收,此时测量的堆内存包含了这些未释放的数据,导致结果偏高。
解决办法
- 找到目标片段后立刻终止响应:避免继续加载剩余数据,直接节省内存
if (chunk.includes(sectionToWatchEnd)) { isDataSectionFound = false; response.destroy(); // 终止响应,停止接收后续数据 }
- 用Buffer替代字符串拼接:Buffer的拼接效率更高,减少内存碎片
function foo(url,sectionToWatchStart,sectionToWatchEnd ) { let isDataSectionFound = false; return new Promise((resolve, reject) => { const buffer = []; // 改用数组存Buffer片段 https .get(url, function (response) { response.on("data", function (chunk) { if (chunk.includes(sectionToWatchStart)) isDataSectionFound = true; if (isDataSectionFound) { buffer.push(chunk); if (chunk.includes(sectionToWatchEnd)) { isDataSectionFound = false; // 拼接Buffer并转为字符串 const result = Buffer.concat(buffer).toString(); resolve(result); response.destroy(); // 终止响应 } } }); response.on("end", function () { resolve(Buffer.concat(buffer).toString()); }); }) .on("error", function (error) { console.error(error); reject(); }); }); }
- 延迟测量内存,触发垃圾回收:给垃圾回收机制时间清理无用数据(需Node.js启动时加
--expose-gc参数)
const mesurMemory = async () => { const initialMemoryUsage = process.memoryUsage().heapUsed; const res = await foo(); // 手动触发垃圾回收 if (typeof global.gc === 'function') { global.gc(); } const finalMemoryUsage = process.memoryUsage().heapUsed; const memoryUsageForCode = finalMemoryUsage - initialMemoryUsage; console.log(`Memory usage for code: ${memoryUsageForCode / (1024 * 1024)}MB`); };
内容的提问来源于stack exchange,提问作者benny hassan
相关产品推荐
相关产品推荐

