设为首页 收藏本站
查看: 988|回复: 0

[经验分享] 浅析nodejs的buffer类(转)

[复制链接]

尚未签到

发表于 2017-2-24 11:15:17 | 显示全部楼层 |阅读模式
  最近翻阅了node v0.10.4的buffer类的源代码,收获不少,也很久没有在cnode上发表文章了,想把一些收获分享给大家,有什么错误的地方希望大牛们指正啊。
  前阵子有位rrestjs框架的使用者YanQ报告给我这样一个错误,跟我说在用户post很多内容的文章时会crash进程然后报如下错误:(热心的老雷帮我解决了问题)
  buffer.js:523
  thrownewRangeError('targetStart out of bounds');
  错误的原因是api上Class Method: Buffer.concat(list, [totalLength])的第二个参数 totalLength 是list中所存储的所有buffer.length的最大小,而不是list的长度,这边大家需要注意下啊。
  言归正传,简单总结一下吧:1、什么时候该用buffer,什么时候不该用我看一下如下的测试代码,分别是拼接各种不同长度的字符串,最后直接拼接了10MB的字符串
  varstring,string2,string3;
  var bufstr,bufstr2,bufstr3;
  var j;
  console.time('write 100 string')
  for(j=0;j<1000;j++){
      var x = j+'';
      string+= x;
  }
  console.timeEnd('write 100 string')
  console.time('write 100 buffer')
  bufstr =newBuffer(100)
  for(j=0;j<1000;j++){
      var x = j+'';
      bufstr.write(x,j);
  }
  console.timeEnd('write 100 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 =newBuffer(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 =newBuffer(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')
  接着是输出结果:
  write 100string:0ms
  write 100 buffer:6ms
  write 100000string:37ms
  write 100000 buffer:150ms
  write 1024*1024*10string:4262ms
  write 1024*1024*10 buffer:8904ms
  读取速度都不需要测试了,肯定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(newBuffer(4096));//申请buf1,占用4096byte空间,会得到自动释放
          //申请buf2,占用几byte空间,将其引用保存在外部数据,不会自动释放
          //*******
          leak_buf_ary.push(newBuffer(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 =newBuffer('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
  最后如果也想看看buffer源码,希望我的博客对你有帮助:浅析node的buffer模块(一创建)浅析node的buffer模块(二写入)浅析node的buffer模块(三读取)
  转自: http://cnodejs.org/topic/5189ff4f63e9f8a54207f60c

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-346583-1-1.html 上篇帖子: nodejs使用multiparty模块实现文件上传(另附express.bodyParser()的说明) 下篇帖子: nodejs搭建简单web服务器!!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表