Buffer
笔记部分内容摘自 《深入浅出 Node.JS》 —— 朴灵
背景
与前端不同,前端常常只需要处理简单字符串操作及DOM操作,而后端需要处理相对复杂的文件、网络I/O等,JS自由的字符串无法满足需求,于是有了buffer以满足后端的需求。
结构
- buffer是JS与C++结合的一个模块,性能相关以C++实现,非性能相关以JS实现。
- buffer由于是C++实现的内建模块,其在node项目进程启动时就已加载进内存,使用时无需require。
- buffer所使用的内存为堆外内存,非V8分配,而是node在C++层面申请,在使用时node再进行分配。
- buffer对象类似于Array,形如<Buffer d3 e2 cd 67 2b 21 8c 9a e7 20 1f 2e 7c 6d 4a cf 3c>,其元素皆为16进程两位数。
使用
1 | var buf = new Buffer(100); |
内存分配
采用slab分配机制
slab就是一块申请好的固定大小的内存区域
具有三种状态:
- full:完全分配状态
- partial:部分分配状态
- empty:没有被分配状态
slab以8k为界限,区分buffer是大对象还是小对象。
1 | Buffer.poolSize = 8 * 1024; |
分配小对象 (<=8kb的buffer)
使用局部变量pool作为中间处理对象,处于分配状态的slab都指向该变量。
1 | var pool; |
- 分配buffer对象会先检查是否有pool对象,若没有,则通过创建新的slab对象单元,并指向它。
- 然后记录下使用的大小used,及此slab使用的偏移量offset。
- 再次创建buffer会判断此slab单元是否足够存放,够则继续存放,不够则重新创建新的slab单元以存储。
- 当slab单元不够存放,重新创建单元时,旧的slab单元则会造成浪费。
一个slab单元内所有的buffer小对象全部都已经释放并可以回收时,才能释放此slab单元。
即若一个slab单元中只存放了1个字节的buffer对象,也会造成整个8kb的slab单元内存无法释放。
分配大对象 (>8kb的buffer)
直接分配一个指定大小(大对象大小)的内存空间:
1 | this.parent = new SlowBuffer(this.length); |
小结
这里的slab分配机制,旨在于减少系统调用,以提升性能。不过对于平时的开发来说,我暂时没看出来有什么作用,算是顶多更加深入的了解了buffer。
Buffer的转换
支持的转换编码类型:
- ASCII
- UTF-8
- UTF-16LE/UCS-2
- Base64
- Binary
- Hex
字符串转Buffer
** 方式: ** 通过构造函数转换。
1 | var buf = new Buffer(str, [encoding]); |
encoding
为可选参数,默认值为UTF-8
。
一个Buffer对象即可以存储多种编码类型的字符串转码,通过write()
方法实现。
1 | buf.write(string, [offset], [length], [encoding]); |
这种存储多种编码类型转码的Buffer,由于每种编码所有的字节长度不同,再解码转回普通字符串时需要格外谨慎小心!
Buffer转字符串
方式: toString()
方法
1 | buf.toString([encoding], [start], [end]); |
encoding
默认UTF-8
不支持编码类型的相互转换
由于Buffer对象支持的编码类型有限,所以借助其它具有相似功能的模块可以更好的完成Buffer的转换。
- iconv : https://www.npmjs.com/package/iconv
- iconv-lite (推荐) : https://www.npmjs.com/package/iconv-lite
Buffer拼接
不正确(危险)的Buffer拼接方式
1 | var fs = require('fs'); |
注意点:
data += chunk;
隐含着buffer转换:data = data.toString() +chunk.toString();
- 当出现宽字节编码,可能会出现乱码问题。
- 在处理UTF-8、Base64或UCS-2/UTF-16LE这三种编码时可以采用添加一条语句来解决此问题:
rs.setEncoding(encoding)
正确的拼接Buffer
思路: 拼接Buffer对象,再用iconv-lite等转换
1 | let chunks = []; |
其中值得一看的是Buffer.concat()
:
1 | Buffer.concat = function(list, length) { |
用Buffer提升性能
- 在web应用中,逻辑代码的编写过程中,通常是在操作字符串,但一旦通过网络传输,都需要进行Buffer转换,以进行二进制数据传输。
- 在不需要改动读取的文件内容的前提下,可以直接读取Buffer并进行传输,尽量减少转换。