JavaScript 对于字符串的操做十分便捷,不管是单字节字符仍是宽字节字符,都会认为是一个字符。对字符串的简单操做和DOM操做基本上已经能够知足前端工程需求,但Node不少时候须要处理文件和网络I/O,就须要处理大量的二进制数据。因此,在Node中就定义了一个Buffer类,该类用来建立一个专门存放二进制数据的缓存区。本文是《深刻浅出nodejs》相关内容的一个总结,是一篇读书笔记,仅供之后查阅。前端
在 Node.js 中,Buffer 类是随 Node 内核一块儿发布的核心库。Buffer 库为 Node.js 带来了一种存储原始数据的方法,可让 Node.js 处理二进制数据,每当须要在 Node.js 中处理I/O操做中移动的数据时,就有可能使用 Buffer 库。原始数据存储在 Buffer 类的实例中。一个 Buffer 相似于一个整数数组,但它对应于 V8 堆内存以外的一块原始内存。node
Buffer是一个相似与Array的对象,但它主要是用于字节操做。也拥有一些相似于Array具备的方法,可使用这些方法进行字节处理。数组
Buffer是一个JavaScript与C++结合的模块,它将性能相关部分使用C++实现,将非性能相关的部分用JavaScript实现。缓存
Buffer所占用的内存不是经过V8分配的,属于堆外内存。因为V8垃圾回收性能的影响,将经常使用的操做对象用更高效和专用的内存分配回收策略来管理是个不错的思路。网络
Buffer是Node的一个核心模块,在进程启动时已经加载,并放置在全局对象中,使用Buffer时,不需require()引入,可直接使用。性能
Buffer对象相似于数组,它的元素为16进制的两位数(如:1F,0~255)。示例以下:ui
不一样编码的字符串占用的元素个数各不相同,ASCII编码的字符占用一个元素,汉字占用两个元素。this
当新建一个Buffer,而没有写入数据时,访问其中的元素,会获得一个0~255的随机值。编码
咱们对Buffer中的一个元素赋值的话,它会默认的转为一个0~255的值,它的转换规则以下:spa
处理大量的字节数据不能采用须要一点内存就向系统申请一点内存的方式,这会形成很大的内存申请的系统调用,对操做系统有必定压力。为此Node在内存的使用上应用的是在C++层面申请,在JavaScript中分配的策略。
为了高效的使用申请来的内存,Node采用了slab分配机制。slab就是一个申请好的固定大小的内存区域,有如下三种状态:
在node中,须要一个Buffer对象,能够经过如下方式指定对象的大小:
new Buffer(size)
Node以8KB为分界来区分Buffer是大对象仍是小对象。在JavaScript层面,以它做为单位单元进行内存的分配。
若是指定Buffer的大小少于8KB,Node会按照小对象的方式进行分配。Buffer的分配过程当中主要使用一个局部变量pool做为中间处理对象,处于分配状态的slab单元都指向它。如下是分配一个全新的slab单元的操做,它会将新申请的SlowBuffer对象指向它:
var pool; function allocPool() { pool = new SlowBuffer(Buffer.poolSize); pool.used = 0; }
slab处于empty状态。
构造小Buffer对象的代码以下:
new Buffer(1024);
此次构造将会去检查pool对象,若是pool没有被建立,将会建立一个新的slab单元指向它:
if(!pool || pool.length - pool.used < this.length) { allocPool(); }
同时当前Buffer对象的parent属性指向该slab,并记录下是从这个slab的哪一个位置(offset)开始使用的,slab对象自身也记录被使用了多少字节,代码以下:
this,parent = pool; this.offset = pool.used; pool.used += this.length; if(pool.used & 7) { pool.used = (pool.used + 8) & ~7; }
这时候的slab状态为partial。当再次建立一个Buffer对象时,构造过程当中将会判断这个slab的剩余空间是否足够。若是足够就使用剩余空间,并更新slab的分配状态。若是剩余空间不足,将会从新构造slab,原slab中剩余的空间会形成浪费。
这就带来了一个问题,一个slab可能分配给多个Buffer对象使用,只有这些Buffer对象在做用域释放并均可以回收时,slab的8KB空间才会被回收。
若是须要超过8KB的Buffer对象,将会直接分配一个SlowBuffer对象做为slab单元,这个单元将会被这个大Buffer对象所独占。
上面提到的Buffer对象都是JavaScript层面上的,可以被V8的垃圾回收标记回收。但其内部的parent属性指向的SlowBuffer对象却来自于Node自身C++中的定义,是C++层面上的Buffer对象,因此内存不在V8的堆中。
Buffer对象能够直接与字符串之间相互转换,目前支持的字符串编码类型有如下几种:
new Buffer(str, [encoding]);
当encoding不传入时,默认为utf-8。一个Buffer对象能够存储不一样编码类型的字符串转码的值,调用write方法能够实现它:
buf.write(string, [offset], [length], [encoding]);
须要特别注意的是,不一样编码所用的字节长度不一样,将Buffer反转回来字符串时须要谨慎处理。
buf.toString([encoding], [start], [end]);
当encoding不传入时,默认为utf-8。能够经过设置encoding,start,end这3个参数实现总体或局部的转换。
Buffer在使用时,一般是一段一段的方式传输。如下是常见的从输入流中读取内容的示例代码:
var fs = require('fs'); var rs = fs.createReadStream('test.md'); var data = ''; rs.on('data', function (chunk){ data += chunk; }); rs.on('end', function (){ console.log(data); });