node.js的buffer类什么时候用,有哪些使用事项要注意
Admin 2022-08-13 群英技术资讯 387 次浏览
1、什么时候该用buffer,什么时候不该用 看一下如下的测试代码,分别是拼接各种不同长度的字符串,最后直接拼接了10MB的字符串
var string,string2,string3; var bufstr,bufstr2,bufstr3; var j; console.time('write 1000 string'); for(j=0;j<1000;j++){ var x = j+''; string += x; } console.timeEnd('write 1000 string'); console.time('write 1000 buffer'); bufstr = new Buffer(1000); for(j=0;j<1000;j++){ var x = j+''; bufstr.write(x,j); } console.timeEnd('write 1000 buffer'); console.time('write 100000 string'); for(j=0;j<100000;j++){ var x = j+''; string2 += x; } console.timeEnd('write 100000 string'); console.time('write 100000 buffer'); bufstr2 = new Buffer(100000) for(j=0;j<100000;j++){ var x = j+''; bufstr2.write(x,j); } console.timeEnd('write 100000 buffer'); console.time('write 1024*1024*10 string'); for(j=0;j<1024*1024*10;j++){ var x = j+''; string3 += x; } console.timeEnd('write 1024*1024*10 string'); console.time('write 1024*1024*10 buffer'); bufstr3 = new Buffer(1024*1024*10); for(j=0;j<1024*1024*10;j++){ var x = j+''; bufstr3.write(x,j); } console.timeEnd('write 1024*1024*10 buffer');
接着是输出结果:
读取速度都不需要测试了,肯定string更快,buffer还需要toString()的操作。 所以我们在保存字符串的时候,该用string还是要用string,就算大字符串拼接string的速度也不会比buffer慢。 那什么时候我们又需要用buffer呢?没办法的时候,当我们保存非utf-8字符串,2进制等等其他格式的时候,我们就必须得使用了。
2、buffer不得不提的8KB
buffer著名的8KB载体,举个例子好比,node把一幢大房子分成很多小房间,每个房间能容纳8个人,为了保证房间的充分使用,只有当一个房间塞满8个人后才会去开新的房间,但是当一次性有多个人来入住,node会保证要把这些人放到一个房间中,比如当前房间A有4个人住,但是一下子来了5个人,所以node不得不新开一间房间B,把这5个人安顿下来,此时又来了4个人,发现5个人的B房间也容纳不下了,只能再开一间房间C了,这样所有人都安顿下来了。但是之前的两间房A和B都各自浪费了4个和3个位置,而房间C就成为了当前的房间。
具体点说就是当我们实例化一个新的Buffer类,会根据实例化时的大小去申请内存空间,如果需要的空间小于8KB,则会多一次判定,判定当前的8KB载体剩余容量是否够新的buffer实例,如果够用,则将新的buffer实例保存在当前的8KB载体中,并且更新剩余的空间。
我们做个简单的实验,模拟一个比较严重的内存泄露情况:
第一次我们将内存泄漏点那行代码注释掉,运行4分钟后,得到如下打印信息,V8已经自动把我分配的内存释放掉了,free men又回到了开始的数值,第二次我们将泄漏点那行代码放开,让全局变量 leak_buf_ary 始终引用着buffer,同样执行10分钟
var os = require('os'); var leak_buf_ary = []; var show_memory_usage = function(){ //打印系统空闲内存 console.log('free mem : ' + Math.ceil(os.freemem()/(1024*1024)) + 'mb'); } var do_buf_leak = function(){ var leak_char = 'l'; //泄露的几byte字符 var loop = 100000;//10万次 var buf1_ary = [] while(loop--){ buf1_ary.push(new Buffer(4096)); //申请buf1,占用4096byte空间,会得到自动释放 //申请buf2,占用几byte空间,将其引用保存在外部数据,不会自动释放 //******* leak_buf_ary.push(new Buffer(loop+leak_char)); //******* } console.log("before gc") show_memory_usage(); buf1_ary = null; return; } console.log("process start") show_memory_usage() do_buf_leak(); var j =10000; setInterval(function(){ console.log("after gc") show_memory_usage() },1000*60)
第一次结果:
process start free mem : 5362mb before gc free mem : 5141mb after gc free mem : 5163mb after gc free mem : 5151mb after gc free mem : 5148mb after gc free mem : 5556mb
第二次结果:
process start free mem : 5692mb before gc free mem : 4882mb after gc free mem : 4848mb after gc free mem : 4842mb after gc free mem : 4843mb after gc free mem : 4816mb after gc free mem : 4822mb after gc free mem : 4816mb after gc free mem : 4809mb after gc free mem : 4810mb after gc free mem : 4831mb after gc free mem : 4830mb
虽然我们释放了4096byte的buffer,但是由于那几byte的字节没有释放掉,将会造成整个8KB的内存都无法释放,如果继续执行循环最终我们的系统内存将耗尽,程序将crash。同样由于我们是依次循环分配 4096+几 byte内存的,所以每块8KB的内存空间都将浪费409Xbyte,在执行循环之后,我们明显发现第二次的内存占用比第一次要大很多。这里我们将近多出了300MB左右的内存消耗。
3、buffer字符串的连接 我们接受post数据时,node是以流的形式发送上来的,会触发ondata事件,所以我们见到很多代码是这样写的:
var http = require('http'); http.createServer(function (req, res) { var body = ''; req.on('data',function(chunk){ //console.log(Buffer.isBuffer(chunk)) body +=chunk }) req.on('end',function(){ console.log(body) res.writeHead(200, {'Content-Type': 'text/plain'}); res.end('Hello World\n'); }) }).listen(8124); console.log('Server running at http://127.0.0.1:8124/');
下面我们比较一下两者的性能区别,测试代码:
var buf = new Buffer('nodejsv0.10.4&nodejsv0.10.4&nodejsv0.10.4&nodejsv0.10.4&'); console.time('string += buf') var s = ''; for(var i=0;i<10000;i++){ s += buf; } s; console.timeEnd('string += buf') console.time('buf concat') var list = []; var len=0; for(var i=0;i<10000;i++){ list.push(buf); len += buf.length; } var s2 = Buffer.concat(list, len).toString(); console.timeEnd('buf concat')
输出结果,相差近一倍:
string += buf: 15ms buf concat: 8ms
在1000次拼接过程中,两者的性能几乎相差一倍,而且当客户上传的是非UTF8的字符串时,直接+=还容易出现错误。
4、独享的空间 如果你想创建一个独享的空间,独立的对这块内存空间进行读写,有两种办法,1是实例化一个超过8KB长度的buffer,另外一个就是使用slowbuffer类。
5、buffer的释放 很遗憾,我们无法手动对buffer实例进行GC,只能依靠V8来进行,我们唯一能做的就是解除对buffer实例的引用。
6、清空buffer 刷掉一块buffer上的数据最快的办法是buffer.fill
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
这篇文章主要介绍了实现一个简单得数据响应系统,文章介绍的数据响应系统会用到Dep,其实,这就是一个依赖收集的容器, depend 收集依赖, notify 触发依赖,下面来看看详细的内容结介绍,需要的朋友可以参考一下
Node.js中怎么使用Redis?下面本篇文章给大家介绍一下Node.js中使用Redis的方法,你会发现原来这么简单,希望对大家有所帮助!
本文主要介绍了react拖拽组件react-sortable-hoc的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
我们通常会用到脚手架vuecli创建项目,此时需要安装脚手架,下面这篇文章主要给大家介绍了关于vue脚手架安装以及安装失败问题的解决办法,需要的朋友可以参考下
这篇文章主要介绍了Vite比Vue CLI快在哪里,帮助大家更好的理解和使用vue,感兴趣的朋友可以了解下
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008