Skip to content

说说对 Buffer 的理解、应用场景 #118

Open
@TieMuZhen

Description

@TieMuZhen

一、是什么

Node应用中,需要处理网络协议、操作数据库、处理图片、接收上传文件等,在网络流和文件的操作中,要处理大量二进制数据,而Buffer就是在内存中开辟一片区域(初次初始化为8KB),用来存放二进制数据

在上述操作中都会存在数据流动,每个数据流动的过程中,都会有一个最小或最大数据量

如果数据到达的速度比进程消耗的速度快,那么少数早到达的数据会处于等待区等候被处理。反之,如果数据到达的速度比进程消耗的数据慢,那么早先到达的数据需要等待一定量的数据到达之后才能被处理

这里的等待区就指的缓冲区(Buffer),总结起来一句话 Node.js 可以用来处理二进制流数据或者与之进行交互

简单来讲,Nodejs不能控制数据传输的速度和到达时间,只能决定何时发送数据,如果还没到发送时间,则将数据放在Buffer中,即在RAM中,直至将它们发送完毕

二、Buffer内存机制

Node.js的垃圾回收中主要使用V8来管理,下面让我们来了解Buffer的内存回收机制。
由于Buffer需要处理的是大量的二进制数据,假如用一点就向系统去申请,则会造成频繁的向系统申请内存调用,所以Buffer所占用的内存不再由V8分配,而是在Node.jsC++层面完成申请,在JavaScript中进行内存分配。因此,这部分内存我们称之为 堆外内存

Node.js采用了 slab机制进行预先申请、事后分配,是一种动态的管理机制
使用 Buffer.alloc(size) 传入一个指定的 size 就会申请一块固定大小的内存区域,slab 具有如下三种状态:

  • full:完全分配状态
  • partial:部分分配状态
  • empty:没有被分配状态

Buffer 内存分配总结

这块内容着实难理解,翻了几本 Node.js 相关书籍,朴灵大佬的「深入浅出 Node.js」Buffer 一节还是讲解的挺详细的,推荐大家去阅读下。

  1. 在初次加载时就会初始化 1 个 8KB 的内存空间,buffer.js 源码有体现
  2. 根据申请的内存大小分为 小 Buffer 对象大 Buffer 对象
  3. 小 Buffer 情况,会继续判断这个slab空间是否足够
    • 如果空间足够就去使用剩余空间同时更新slab分配状态,偏移量会增加
    • 如果空间不足,slab 空间不足,就会去创建一个新的slab空间用来分配
  4. 大 Buffer 情况,则会直接走 createUnsafeBuffer(size) 函数
  5. 不论是小 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的第二个参数keystringBuffer类型

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);
// 你好

参考文献

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions