Description
一、是什么
在Node
应用中,需要处理网络协议、操作数据库、处理图片、接收上传文件等,在网络流和文件的操作中,要处理大量二进制数据,而Buffer
就是在内存中开辟一片区域(初次初始化为8KB
),用来存放二进制数据
在上述操作中都会存在数据流动,每个数据流动的过程中,都会有一个最小或最大数据量
如果数据到达的速度比进程消耗的速度快,那么少数早到达的数据会处于等待区等候被处理。反之,如果数据到达的速度比进程消耗的数据慢,那么早先到达的数据需要等待一定量的数据到达之后才能被处理
这里的等待区就指的缓冲区(Buffer
),总结起来一句话 Node.js 可以用来处理二进制流数据或者与之进行交互 。
简单来讲,Nodejs
不能控制数据传输的速度和到达时间,只能决定何时发送数据,如果还没到发送时间,则将数据放在Buffer
中,即在RAM
中,直至将它们发送完毕
二、Buffer内存机制
在Node.js
的垃圾回收中主要使用V8
来管理,下面让我们来了解Buffer
的内存回收机制。
由于Buffer
需要处理的是大量的二进制数据,假如用一点就向系统去申请,则会造成频繁的向系统申请内存调用,所以Buffer
所占用的内存不再由V8
分配,而是在Node.js
的C++
层面完成申请,在JavaScript
中进行内存分配。因此,这部分内存我们称之为 堆外内存 。
Node.js
采用了 slab
机制进行预先申请、事后分配,是一种动态的管理机制 。
使用 Buffer.alloc(size) 传入一个指定的 size 就会申请一块固定大小的内存区域,slab 具有如下三种状态:
- full:完全分配状态
- partial:部分分配状态
- empty:没有被分配状态
Buffer 内存分配总结
这块内容着实难理解,翻了几本 Node.js 相关书籍,朴灵大佬的「深入浅出 Node.js」Buffer 一节还是讲解的挺详细的,推荐大家去阅读下。
- 在初次加载时就会初始化 1 个 8KB 的内存空间,buffer.js 源码有体现
- 根据申请的内存大小分为 小 Buffer 对象 和 大 Buffer 对象
- 小 Buffer 情况,会继续判断这个
slab
空间是否足够- 如果空间足够就去使用剩余空间同时更新
slab
分配状态,偏移量会增加 - 如果空间不足,slab 空间不足,就会去创建一个新的
slab
空间用来分配
- 如果空间足够就去使用剩余空间同时更新
- 大 Buffer 情况,则会直接走 createUnsafeBuffer(size) 函数
- 不论是小 Buffer 对象还是大 Buffer 对象,内存分配是在
C++
层面完成,内存管理在 JavaScript 层面,最终还是可以被 V8 的垃圾回收标记所回收。
三、应用场景
Buffer的应用场景常常与流的概念联系在一起,例如有如下:
- I/O操作
- 加密解密
- zlib.js
I/O操作
通过流的形式,将一个文件的内容读取到另外一个文件
const fs = require('fs');
const inputStream = fs.createReadStream('input.txt'); // 创建可读流
const outputStream = fs.createWriteStream('output.txt'); // 创建可写流
inputStream.pipe(outputStream); // 管道读写
加解密
在一些加解密算法中会遇到使用 Buffer,例如crypto.createCipheriv
的第二个参数key
为string
或Buffer
类型
zlib.js
zlib.js
为 Node.js 的核心库之一,其利用了缓冲区(Buffer)的功能来操作二进制数据流,提供了压缩或解压功能
四、使用方法
Buffer 类在全局作用域中,无须require导入
创建Buffer的方法有很多种,我们讲讲下面的两种常见的形式:
- Buffer.from()
- Buffer.alloc()
Buffer.from()
const b1 = Buffer.from('10');
const b2 = Buffer.from('10', 'utf8');
const b3 = Buffer.from([10]);
const b4 = Buffer.from(b3);
console.log(b1, b2, b3, b4); // <Buffer 31 30> <Buffer 31 30> <Buffer 0a> <Buffer 0a>
Buffer.alloc()
const bAlloc1 = Buffer.alloc(10); // 创建一个大小为 10 个字节的缓冲区
const bAlloc2 = Buffer.alloc(10, 1); // 建一个长度为 10 的 Buffer,其中全部填充了值为 `1` 的字节
console.log(bAlloc1); // <Buffer 00 00 00 00 00 00 00 00 00 00>
console.log(bAlloc2); // <Buffer 01 01 01 01 01 01 01 01 01 01>
在上面创建buffer
后,则能够toString
的形式进行交互,默认情况下采取utf8
字符编码形式,如下
const buffer = Buffer.from("你好");
console.log(buffer);
// <Buffer e4 bd a0 e5 a5 bd>
const str = buffer.toString();
console.log(str);
// 你好