为何blob.arrayBuffer占用的内存无法自动释放?
我编写了一段将客户端摄像头输入录制到服务器的代码:
async function saveIntoMp4(chunks) { const options = { type: "video/webm" } let blob = new Blob(chunks, options); chunks.length = 0 // 防止内存泄漏 const buffer = Buffer.from(await blob.arrayBuffer()); try { fs.writeFile( `./videos/1.mp4`, buffer, () => console.log("video is saved!") ); } catch (error) { console.log(error) } }
但在Ubuntu 20.04.6 LTS系统中,每次调用该函数内存占用都会持续增加,buffer占用的内存始终无法释放。我未保留buffer、chunks、blob等对象的引用,使用Node.js 18.9和20版本均未解决问题,请问为何blob.arrayBuffer()无法释放占用的内存?
可能的原因及解决方案
1. 异步写入导致Buffer被闭包长期持有
你使用的是异步版fs.writeFile,在回调函数执行完成前,buffer会被闭包引用,无法被垃圾回收(GC)。如果录制频率较高,大量未回收的Buffer会堆积在内存中,导致占用持续上升。
解决办法:改用同步写入或Promise版写入,并显式解除引用:
// 方案1:同步写入 try { fs.writeFileSync(`./videos/1.mp4`, buffer); console.log("video is saved!"); } catch (error) { console.log(error); } // 方案2:Promise版异步写入+显式置空 try { await fs.promises.writeFile(`./videos/1.mp4`, buffer); console.log("video is saved!"); buffer = null; // 主动解除引用,帮助GC回收 } catch (error) { console.log(error); }
2. Blob转Buffer产生冗余内存拷贝
Node.js中,Blob.arrayBuffer()会创建一份内存拷贝,之后Buffer.from()又会创建第二份拷贝。这两份拷贝可能因GC标记延迟无法及时回收,尤其在处理大视频片段时更明显。
解决办法:跳过Blob环节,直接将chunks合并为Buffer,避免中间拷贝:
async function saveIntoMp4(chunks) { // 直接将chunks(假设为Uint8Array数组)合并为Buffer const buffer = Buffer.concat(chunks.map(chunk => Buffer.from(chunk))); chunks.length = 0; // 清空原数组 try { await fs.promises.writeFile(`./videos/1.mp4`, buffer); console.log("video is saved!"); buffer = null; } catch (error) { console.log(error); } }
3. Linux系统内存缓存的误导
Ubuntu系统默认会将空闲内存用作文件缓存,top或htop显示的高内存占用可能包含缓存部分,并非真正的内存泄漏。可以用free -h命令查看实际占用:
free -h
输出中used列排除buff/cache后的数值才是进程实际占用的内存。如果确认是内存泄漏,可通过Node.js调试工具排查:
node --inspect your-app.js
打开Chrome浏览器访问chrome://inspect,连接到Node进程后,在Memory面板生成多次函数调用后的堆快照,对比快照找到未被回收的对象。
4. 重复写入同一文件的潜在问题
每次都写入./videos/1.mp4,若文件被其他进程锁定(比如前一次写入未完成、文件被打开查看),可能导致相关内存资源无法释放。可以尝试使用唯一文件名测试:
const filename = `./videos/${Date.now()}.mp4`; await fs.promises.writeFile(filename, buffer);
内容的提问来源于stack exchange,提问作者Hypothesis

