栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么尝试写入大文件会导致js堆内存不足

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么尝试写入大文件会导致js堆内存不足

发生内存不足错误是因为您没有等待

drain
事件的发出,而没有等待Node.js将缓冲所有写入的块,直到出现最大的内存使用量为止。

.write``false
如果内部缓冲区大于
highWaterMark
默认值16384字节(16kb),将返回。在您的代码中,您没有处理的返回值
.write
,因此永远不会刷新缓冲区。

使用以下命令可以很容易地测试它:

tail -f test.dat

执行脚本时,您会看到

test.dat
直到脚本完成为止都没有任何内容在写。

对于

1e7
缓冲区应清除610次。

1e7 / 16384 = 610

一种解决方案是检查

.write
返回值,如果
false
返回,请使用
file.once('drain')
包装在promise中的包,直到
drain
事件被发出为止。

注意:

writable.writableHighWaterMark
已在节点v9.3.0中添加

const file = require("fs").createWriteStream("./test.dat");(async() => {    for(let i = 0; i < 1e7; i++) {        if(!file.write('a')) { // Will pause every 16384 iterations until `drain` is emitted await new Promise(resolve => file.once('drain', resolve));        }    }})();

现在,如果您这样做,

tail -f test.dat
您将看到脚本仍在运行时如何写入数据。


关于为什么出现1e7而不是1e6的内存问题的原因,我们必须研究一下Node.Js如何进行缓冲,这发生在writeOrBuffer函数中。

此示例代码将使我们对内存使用情况有一个大概的估计:

const count = Number(process.argv[2]) || 1e6;const state = {};function nop() {}const buffer = (data) => {    const last = state.lastBufferedRequest;    state.lastBufferedRequest = {      chunk: Buffer.from(data),      encoding: 'buffer',      isBuf: true,      callback: nop,      next: null    };    if(last)      last.next = state.lastBufferedRequest;    else      state.bufferedRequest = state.lastBufferedRequest;    state.bufferedRequestCount += 1;}const start = process.memoryUsage().heapUsed;for(let i = 0; i < count; i++) {    buffer('a');}const used = (process.memoryUsage().heapUsed - start) / 1024 / 1024;console.log(`${Math.round(used * 100) / 100} MB`);

执行时:

// node memory.js <count>1e4: 1.98 MB1e5: 16.75 MB1e6: 160 MB5e6: 801.74 MB8e6: 1282.22 MB9e6: 1442.22 MB - Out of memory1e7: 1602.97 MB - Out of memory

因此,每个对象都使用

~0.16kb
,并且在
writes
不等待
drain
事件的情况下执行1e7时,您的内存中就有1000万个对象(公平地说,它在达到10M之前就崩溃了)

不管使用单个

a
还是1000,存储的增加都可以忽略不计。


您可以使用

--max_old_space_size={MB}
flag 来增加节点使用的最大内存
(当然这不是解决方案,仅用于检查内存消耗而不使脚本崩溃)

node --max_old_space_size=4096 memory.js 1e7

更新 :我在内存片段上犯了一个错误,导致内存使用量增加了30%。我正在为每个

.write
Node重用
nop
回调创建一个新的回调。


更新二

如果您始终写入相同的值(在实际情况下是可疑的),则可以通过每次传递相同的缓冲区来 大大 减少内存使用和执行时间:

const buf = Buffer.from('a');for(let i = 0; i < 1e7; i++) {    if(!file.write(buf)) {        // Will pause every 16384 iterations until `drain` is emitted        await new Promise(resolve => file.once('drain', resolve));    }}


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/595025.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号