最近翻阅了node v0.10.4的buffer类的源代码,收获不少,也很久没有在cnode上发表文章了,想把一些收获分享给大家,有什么错误的地方希望大牛们指正啊。

前阵子有位rrestjs框架的使用者YanQ报告给我这样一个错误,跟我说在用户post很多内容的文章时会crash进程然后报如下错误:(热心的老雷帮我解决了问题)

buffer.js:523

throw
new
RangeError('targetStart out of bounds');

错误的原因是api上Class Method: Buffer.concat(list, [totalLength])的第二个参数 totalLength 是list中所存储的所有buffer.length的最大小,而不是list的长度,这边大家需要注意下啊。

言归正传,简单总结一下吧:
1、什么时候该用buffer,什么时候不该用
我看一下如下的测试代码,分别是拼接各种不同长度的字符串,最后直接拼接了10MB的字符串

var
string,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 =
new
Buffer(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 =
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')

接着是输出结果:

write 100
string:
0ms

write 100 buffer:
6ms

write 100000
string:
37ms

write 100000 buffer:
150ms

write 1024*1024*10
string:
4262ms

write 1024*1024*10 buffer:
8904ms

读取速度都不需要测试了,肯定string更快,buffer还需要toString()的操作。
所以我们在保存字符串的时候,该用string还是要用string,就算大字符串拼接string的速度也不会比buffer慢。
那什么时候我们又需要用buffer呢?没办法的时候,当我们保存非utf-8字符串,2进制等等其他格式的时候,我们就必须得使用了。

、buffer不得不提的8KB

个人,为了保证房间的充分使用,只有当一个房间塞满8个人后才会去开新的房间,但是当一次性有多个人来入住,node会保证要把这些人放到一个房间中,比如当前房间A有4个人住,但是一下子来了5个人,所以node不得不新开一间房间B,把这5个人安顿下来,此时又来了4个人,发现5个人的B房间也容纳不下了,只能再开一间房间C了,这样所有人都安顿下来了。但是之前的两间房A和B都各自浪费了4个和3个位置,而房间C就成为了当前的房间。

具体点说就是当我们实例化一个新的Buffer类,会根据实例化时的大小去申请内存空间,如果需要的空间小于8KB,则会多一次判定,判定当前的8KB载体剩余容量是否够新的buffer实例,如果够用,则将新的buffer实例保存在当前的8KB载体中,并且更新剩余的空间。

我们做个简单的实验,模拟一个比较严重的内存泄露情况:

分钟后,得到如下打印信息,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左右的内存消耗。

、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

次拼接过程中,两者的性能几乎相差一倍,而且当客户上传的是非UTF8的字符串时,直接+=还容易出现错误。

、独享的空间
如果你想创建一个独享的空间,独立的对这块内存空间进行读写,有两种办法,1是实例化一个超过8KB长度的buffer,另外一个就是使用slowbuffer类。

、buffer的释放
很遗憾,我们无法手动对buffer实例进行GC,只能依靠V8来进行,我们唯一能做的就是解除对buffer实例的引用。

、清空buffer
刷掉一块buffer上的数据最快的办法是buffer.fill

最后如果也想看看buffer源码,希望我的博客对你有帮助:
浅析node的buffer模块(一创建)
浅析node的buffer模块(二写入)
浅析node的buffer模块(三读取)

转自: http://cnodejs.org/topic/5189ff4f63e9f8a54207f60c

浅析nodejs的buffer类(转)的更多相关文章

  1. 浅析nodejs的buffer类

    1.什么时候该用buffer,什么时候不该用 看一下如下的测试代码,分别是拼接各种不同长度的字符串,最后直接拼接了10MB的字符串 var string,string2,string3; var bu ...

  2. 关于golang中IO相关的Buffer类浅析

    io重要的接口 在介绍buffer之前,先来认识两个重要的接口,如下边所示: type Reader interface { Read(p []byte) (n int, err error) } t ...

  3. nodeJS-使用buffer类处理二进制数据

    使用buffer类处理二进制数据 在客户端javascript脚本代码中,对于二进制数据并没有提供一个很好的支持.然后在nodejs中需要处理像TCP流或文件流时,必须要处理二进制数据.因此在node ...

  4. 浅析Java.lang.Process类

    一.概述      Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序).      Process 类提供了执行从进程输入.执行输出到进程.等待进程完成.检查进程的 ...

  5. nodejs: 理解Buffer

    学习nodejs中buffer这一章,有一段写到buffer的拼接,其中一段源码非常优美,特拿来与大家共享. var chunks = []; var size = 0; res.on('data', ...

  6. Node.js权威指南 (5) - 使用Buffer类处理二进制数据

    5.1 创建Buffer对象 / 705.2 字符串的长度与缓存区的长度 / 725.3 Buffer对象与字符串对象之间的相互转换 / 74 5.3.1 Buffer对象的toString方法 / ...

  7. Node.js系列:Buffer类的使用

    客户端JavaScript中没有对二进制数据提供很好的支持.但是在处理TCP流或文件流时,必须要处理二进制数据.Node.js定义了一个Buffer类,用来创建一个专门存放二进制数据的缓存区. Buf ...

  8. Node.js之使用Buffer类处理二进制数据

    Node.js之使用Buffer类处理二进制数据 Buffer类可以在处理TCP流或文件流时处理二进制数据,该类用来创建一个专门存放二进制数据的缓存区. 1. 创建Buffer对象 1.1 直接创建: ...

  9. node.js—Buffer类(二进制数据处理模块)

    Buffer类概述 一个用于更好的操作二进制数据的类 我们在操作文件或者网络数据的时候,其实操作的就是二进制数据流 Node为我们提供了一个更加方便的去操作这种数据流的类 Buffer,他是一个全局的 ...

随机推荐

  1. Vue slot插槽内容分发

    slot插槽使用 使用场景,一般父组件中又一大段模板内容需要运用到子组件上.或者更加复杂的,子组件需要运用到父组件大段模板内容,而子组件却不知道挂载的内容是什么.挂载点的内容是由父组件来决定的. Sl ...

  2. codeforces#1136 C. Nastya Is Transposing Matrices(找规律)

    题意:给出两个n*m的矩阵,每次操作可以让一个正方形矩阵行列交换.问,在无限次操作下,第一个矩阵能否变成第二个矩阵 分析:先把操作限定在2*2的矩阵中.这样对角线上的元素就可以随意交换.也就是说,如果 ...

  3. ORM框架SQLAlchemy

    SQLAlchemy orm英文全称object relational mapping,就是对象映射关系程序,简单来说就是类似python这种面向对象的程序来说一切皆对象,但是使用的数据库却都是关系型 ...

  4. Linux 默认连接数

    Linux 默认连接数 - 国内版 Binghttps://cn.bing.com/search?FORM=U227DF&PC=U227&q=Linux+%E9%BB%98%E8%AE ...

  5. lr12 websocket

    loadrunner12以上版本支持websocket,在http/html协议录制时可以直接录制websocket相关内容信息. 网上找的一个测试websocket网址:http://www.blu ...

  6. git中如何撤销部分修改?

    以提问中修改了两个文件a.b为例,假设需要撤销文件a的修改,则修改后的两个文件: 1.如果没有被git add到索引区 git checkout a 便可撤销对文件a的修改 2.如果被git add到 ...

  7. iView组件添加API中介绍的事件的方式(render方式添加事件)

    iView组件好用,文档齐全,品质可靠稳定.最大的好处是使用了Vue框架,使很多数据绑定和交互问题变的轻松,是难得的开源前端组件.给作者点个赞.用这个组件来学习Vue.js也是不错的选择. 最近用的比 ...

  8. Flask 构建微电影视频网站(四)

    后台管理 实现后台管理系统使用flask sqlalchemy结合mysql数据库进行增删改查操作.分页的使用.路由装饰器定义.模板中变量调用.登录会话机制.上传文件.flask wtforms表单使 ...

  9. jforum(1)--环境搭建

    JForum 是采用Java开发的功能强大且稳定的论坛系统.它提供了抽象的接口.高效的论坛引擎以及易于使用的管理界面,同时具有完全的权限控制.多语言支持(包括中文).高性能.可自定义的用户接口.安全. ...

  10. 前端ajax请求百度地图api

    $.ajax({ type: "get", url: 'http://api.map.baidu.com/place/v2/search', data:{ ak:'您的ak', q ...