Node.js Buffer

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。但在处理像TCP流或文件流时,必须使用到二进制数据。

因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。该缓存区对应 V8 堆内存之外的一块原始内存。

Buffer是典型的JS与C++结合的模块。

在Node启动时就会加载它,并挂载到global全局对象上。

Buffer对象

Buffer对象类似于数组,存放的元素为16进制的两位数(00-FF),即0-255,占用8bit,一个字节。

可通过length获得Buffer的长度(字节数)。

Buffer内存分配

在C++层面申请内存,在JS中分配内存。

slab分配机制

是一种动态内存管理机制。

slab是一块已申请的固定大小的内存区域。有3种状态:

  • 没有分配

  • 部分分配

  • 完全分配。

Node以8KB为界限区分Buffer是大对象还是小对象。

8KB是每个slab的大小,在JS层面,以8KB为单位进行内存分配。

小Buffer对象分配

预先申请slab,事后分配。

一个slab可以存储多个小Buffer对象。

大Buffer对象分配

直接分配一个SlowBuffer对象作为slab单元,Buffer对象独占这个slab,不用详细分配。

SlowBuffer对象是在C++中定义的,使用C++层面的内存。

Buffer的API

创建Buffer

Buffer.alloc()

Buffer.from():数字数组、字符串、Buffer

写入数据

buffer.write()

Buffer与字符编码

通过使用显式的字符编码,就可以在 Buffer 实例与普通的 JavaScript 字符串之间进行相互转换。

1
2
3
4
5
const buf = Buffer.from('runoob', 'ascii');
// 输出 72756e6f6f62
console.log(buf.toString('hex'));
// 输出 cnVub29i
console.log(buf.toString('base64'));

支持的字符串编码有限,常见的有:

  • ASCII

  • UTF-8

  • Base64

  • Hex

Buffer的拼接

流传输时,获取的数据是一段一段的,就是一个Buffer对象。

边传输边按字符串拼接Buffer对象容易导致乱码问题。英文不会乱码,因为是单字节的,中文是多字节的,可能会出现乱码。

正确方法是:将多个小Buffer缓存起来,传输完成后,合并成一个大的Buffer,再转换成字符串。

Buffer与性能

Buffer在文件IO和网络IO中运用广泛。

在应用中我们往往是操作字符串,但在网络传输中,都需要转换成Buffer,传输二进制数据。

在Web应用中,字符转换成Buffer是时时刻刻发生的,提高String转Buffer的效率,可以提高网络吞吐率。

(1)通过预先将静态内容转换成Buffer对象,可以减少CPU重复利用,不用每次响应执行一个Buffer转换。因此,在Web应用中,把动态内容和静态内容分离,静态内容预先转换成Buffer对象。

(2)读取大文件时,highWaterMark越大,读取速度越快。

1
fs.createReadStream(path, {});

highWaterMark对Buffer内存的分配和使用有影响。