浅析nodejs的buffer类
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
浅析nodejs的buffer类的更多相关文章
- 浅析nodejs的buffer类(转)
最近翻阅了node v0.10.4的buffer类的源代码,收获不少,也很久没有在cnode上发表文章了,想把一些收获分享给大家,有什么错误的地方希望大牛们指正啊. 前阵子有位rrestjs框架的使用 ...
- 关于golang中IO相关的Buffer类浅析
io重要的接口 在介绍buffer之前,先来认识两个重要的接口,如下边所示: type Reader interface { Read(p []byte) (n int, err error) } t ...
- nodeJS-使用buffer类处理二进制数据
使用buffer类处理二进制数据 在客户端javascript脚本代码中,对于二进制数据并没有提供一个很好的支持.然后在nodejs中需要处理像TCP流或文件流时,必须要处理二进制数据.因此在node ...
- 浅析Java.lang.Process类
一.概述 Process类是一个抽象类(所有的方法均是抽象的),封装了一个进程(即一个执行程序). Process 类提供了执行从进程输入.执行输出到进程.等待进程完成.检查进程的 ...
- nodejs: 理解Buffer
学习nodejs中buffer这一章,有一段写到buffer的拼接,其中一段源码非常优美,特拿来与大家共享. var chunks = []; var size = 0; res.on('data', ...
- Node.js权威指南 (5) - 使用Buffer类处理二进制数据
5.1 创建Buffer对象 / 705.2 字符串的长度与缓存区的长度 / 725.3 Buffer对象与字符串对象之间的相互转换 / 74 5.3.1 Buffer对象的toString方法 / ...
- Node.js系列:Buffer类的使用
客户端JavaScript中没有对二进制数据提供很好的支持.但是在处理TCP流或文件流时,必须要处理二进制数据.Node.js定义了一个Buffer类,用来创建一个专门存放二进制数据的缓存区. Buf ...
- Node.js之使用Buffer类处理二进制数据
Node.js之使用Buffer类处理二进制数据 Buffer类可以在处理TCP流或文件流时处理二进制数据,该类用来创建一个专门存放二进制数据的缓存区. 1. 创建Buffer对象 1.1 直接创建: ...
- node.js—Buffer类(二进制数据处理模块)
Buffer类概述 一个用于更好的操作二进制数据的类 我们在操作文件或者网络数据的时候,其实操作的就是二进制数据流 Node为我们提供了一个更加方便的去操作这种数据流的类 Buffer,他是一个全局的 ...
随机推荐
- 什么是WSDL
WSDL定义 WSDL(Web Service Description Language) 指网络服务描述语言:是一种用来描述Web服务和说明Web服务通信的XML.WSDL用于描述WebServic ...
- Winform 连接Oracle10g时出错的解决方法
环境:Win7(64位). VS2012 update3.Oracle10 (本机已安装ODTwithODAC1120320_32bit) 最近在开发一程序时莫名其妙报一个错误(未能加载文件或程序集“ ...
- iOS学习笔记48-Swift(八)反射
一.Swift反射 所谓反射就是可以动态获取类型.成员信息,在运行时可以调用方法.属性等行为的特性. 在使用OC开发时很少强调其反射概念,因为OC的Runtime要比其他语言中的反射强大的多.不过在S ...
- HDU-1528/1962 Card Game Cheater
两组牌中两张牌相比能赢的就连,后求最大匹配. #include <cmath> #include <cstdlib> #include <cstdio> #incl ...
- POJ3071 Football 【概率dp】
题目 Consider a single-elimination football tournament involving 2n teams, denoted 1, 2, -, 2n. In eac ...
- 升级springboot 2.x 踩过的坑——跨域导致session问题
目前IT界主流前后端分离,但是在分离过程中一定会存在跨域的问题. 什么是跨域? 是指浏览器从一个域名的网页去请求另一个域名的资源时,域名.端口.协议任一不同,都是跨域. 做过web后台的童鞋都知道,跨 ...
- UVA 116 Unidirectional TSP(DP最短路字典序)
Description Unidirectional TSP Background Problems that require minimum paths through some domai ...
- Java手机游戏开发简明教程 (SunJava开发者认证程序员 郎锐)
原文发布时间为:2008-07-30 -- 来源于本人的百度文章 [由搬家工具导入] Java手机游戏开发实例简明教程 (SunJava开发者认证程序员 郎锐)一、手机游戏编写基础1.手机游戏设计的基 ...
- input弹出的手机键盘搜索事件
一.input的搜索框 在input标签里面把type设置为search就可以了.弹出的手机键盘回车键也会变成搜索或者是搜索的图标. <input id="search" ...
- linux 头文件以及库的路径
原来在编译的时候可以指定执行时去哪里找需要的lib文件,长知识了 本文详细介绍了Linux 下gcc头文件指定方法,以及搜索路径顺序的问题.另外,还总结了,gcc动态链接的方法以及路径指定,同样也讨论 ...