Node.js - Buffer缓冲器

三W法则
Buffer是什么?Buffer什么时候用?Buffer怎么用?

Buffer是什么?

官方回答

JavaScript 语言没有读取或操作二进制数据流的机制。 Buffer 类被引入作为 Node.js API 的一部分,使其可以在 TCP** 流或文件**系统操作等场景中处理二进制数据流。


Buffer是在内存中开辟的一片区域,用于存放二进制数据。Buffer所开辟的是堆外内存

引申:
堆外内存(off-heap memory
数据流(Stream

堆外内存(off-heap memory)

堆外内存就是把内存对象分配在虚拟机的堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机),这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。

Stream流

1,计算机将字符存储成二进制的方式,计算机会将无论图片、视频或其他数据都转换为二进制并存储,这就二进制数据

在Node.js中,流(stream)就是一系列从A点到B点移动的数据,当你有一个很大的数据需要传输、搬运、处理时,你不需要等待所有数据都传输完成才开始下一步工作。

大量的数据会被分割成小块(chunks)进行传输,如果处理数据的时间比到达的时间快,这一时刻仅仅到达了一小部分数据,那这小部分数据需要等待剩下的数据填满,然后再送过去统一处理。这个"等待区域"就是buffer(缓冲池)


现实中:线看视频
网络足够快,数据流(stream)就可以足够快,可以让buffer迅速填满然后发送和处理,然后处理另一个,再发送,再另一个,再发送,然后整个stream完成。

当你网络连接很慢,当处理完当前的数据后,你的播放器就会暂停,或出现"缓冲"(buffer)字样,意思是正在收集更多的数据,或者等待更多的数据到来,才能下一步处理。当buffer装满并处理好,播放器就会显示数据,也就是播放视频了。在播放当前内容的时候,更多的数据也会源源不断的传输、到达和在buffer等待。


总结:Buffer类被引入到Node.js的API中,让其可以和二进制数据流交互和操作。

Buffer什么时候用?

Stream流

流的数据不能一次性获取到,数据也不会全部load到内存中,因此流非常适合大数据处理以及断断续续返回chunk的外部源。流的生产者与消费者之间的速度通常是不一致的,因此需要buffer来暂存一些数据。buffer大小通过highWaterMark参数指定,默认情况下是16Kb。

存储的的数据占用大量内存

Buffer 对象占用的内存空间是不计算在 Node.js 进程内存空间限制上的,所以可以用来存储大对象,但是对象的大小还是有限制的。一般情况下32位系统大约是1G,64位系统大约是2G。

总结:
1,流的处理(不能一次性获取到)
2,存储的数据需要占用很大内存时

Buffer怎么用?


创建buffer

Buffer作为存在于全局对象上,不需要引入模块即可使用

存储的数据已确定

Buffer.from(obj) // obj支持的类型string, buffer, arrayBuffer, array, or array-like object
const buf = Buffer.from([1, 2, 3, 4]);
console.log(buf); //  <Buffer 01 02 03 04>
//Buffer.from不支持传入数字

数据未确定

Buffer.alloc、Buffer.allocUnsafe、Buffer.allocUnsafeSlow

1、Buffer.alloc**(size[, fill[, encoding]])**

  • size 新 Buffer 的所需长度。
  • fill | | | 用于预填充新 Buffer 的值。默认值: 0
  • encoding 如果 fill 是一个字符串,则这是它的字符编码。默认值: 'utf8'
const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');
console.log(buf);
// 打印: <Buffer 68 65 6c 6c 6f 20 77 6f 72 6c 64>

const buf = Buffer.alloc(5);

console.log(buf);
// 打印: <Buffer 00 00 00 00 00>

优点:确保新创建的 Buffer 实例的内容永远不会包含来自先前分配的敏感数据
缺点:调用 Buffer.alloc() 可能比替代的 Buffer.allocUnsafe() 慢得多(以零初始化)

2、Buffer.allocUnsafe(size)

const buf = Buffer.allocUnsafe(10);

console.log(buf);
// 打印(内容可能有所不同): <Buffer a0 8b 28 3f 01 00 00 00 50 32>

buf.fill(0);

console.log(buf);
// 打印: <Buffer 00 00 00 00 00 00 00 00 00 00>
  1. 这种方式创建的 Buffer 实例的底层内存是未初始化的
  2. Buffer 模块会预分配一个内部的大小为 Buffer.poolSize 的 Buffer 实例,作为快速分配的内存池,用于使用 Buffer.allocUnsafe() 创建新的 Buffer 实例
  3. Buffer.alloc(size, fill) 不会使用内部的 Buffer 池,而 Buffer.allocUnsafe(size).fill(fill) 在 size 小于或等于 Buffer.poolSize 的一半时将会使用内部的 Buffer池。 该差异虽然很微妙,但当应用程序需要 Buffer.allocUnsafe() 提供的额外性能时,则非常重要

3、Buffer.allocUnsafeSlow(size)

// 需要保留一小块内存。
const store = [];

socket.on('readable', () => {
  let data;
  while (null !== (data = readable.read())) {
    // 为剩下的数据分配内存。
    const sb = Buffer.allocUnsafeSlow(10);

    // 拷贝数据到新分配的内存。
    data.copy(sb, 0, 0, 10);

    store.push(sb);
  }
});
  1. 这种方式创建的 Buffer 实例的底层内存是未初始化的
  2. 相对Buffer.allocUnsafe,如果要分配的内存小于 4KB,则会从一个预分配的 Buffer 切割出来
  3. 避免垃圾回收机制因创建太多独立的 Buffer 而过度使用,通过消除跟踪和清理尽可能多的单个 ArrayBuffer 对象的需要,该方法可以提高性能和内存使用率。

对比:
1,Buffer.alloc会用0值填充已分配的内存,所以相比后两者速度上要慢,但是也较为安全

2,当分配的空间小于4KB的时候,allocUnsafe会直接从之前预分配的Buffer里面slice空间,因此速度allocUnsafe比allocUnsafeSlow要快,当大于等于4KB的时候二者速度相差无异。
在这里插入图片描述

Buffer使用

buf.toString([encoding[, start[, end]]])
const buf = Buffer.from('buffer');
console.log(buf.toString('utf8'));                 // buffer
console.log(buf.toString('utf8', 0, 2));           // bu

buf.toJSON()
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
console.log(buf.toJSON());    // { type: 'Buffer', data: [ 1, 2, 3, 4, 5 ] }

buf.slice([start[, end]])

buffer裁剪,裁剪后返回的新的buffer与原buffer指向同一块内存

buf.slice([start[, end]])
start 起始位置
end 结束位置(不包含)

var buf1 = Buffer.from('buff');
var buf2 = buf1.slice(1, 3).fill('xx');
console.log("buf2 content: " + buf2.toString()); // xx
console.log("buf1 content: " + buf1.toString()); // bxxf

buffer拷贝,buffer与数组不同,buffer的长度一旦确定就不再变化,因此当拷贝的源buffer比目标buffer大时只会复制部分的值

buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

示例:
var buf1 = Buffer.from('123456789');
var buf2 = Buffer.from('ABCDEF');

buf1.copy(buf2, 1);
console.log(buf2.toString()); //A12345

buf.compare对比

buf.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])

  • target | 要与 buf 对比的 Buffer 或 Uint8Array
  • targetStart target 中开始对比的偏移量。默认值: 0。
  • targetEnd target 中结束对比的偏移量(不包含)。默认值: target.length。
  • sourceStart buf 中开始对比的偏移量。默认值: 0。
  • sourceEnd buf 中结束对比的偏移量(不包含)。默认值: buf.length
  • 返回:

对比 buf 与 target,并返回一个数值,表明 buf 在排序上是否排在 target 前面、或后面、或相同。 对比是基于各自 Buffer 实际的字节序列。

  • 如果 target 与 buf 相同,则返回 0。
  • 如果 target 排在 buf 前面,则返回 1。
  • 如果 target 排在 buf 后面,则返回 -1。

主要的作用是用于对数组内的buffer实例排序

buf.equals(otherBuffer) 相当于 buf.compare(otherBuffer) === 0

如果 buf 与 otherBuffer 具有完全相同的字节,则返回 true,否则返回 false
比较的是二进制值

const buf1 = Buffer.from('ABC');
const buf2 = Buffer.from('414243', 'hex');
const buf3 = Buffer.from('ABCD');

console.log(buf1.equals(buf2));
// 打印: true
console.log(buf1.equals(buf3));
// 打印: false

buf.fill 赋值

buf.fill(value[, offset[, end]][, encoding])

包含特定值
buf.includes(value[, byteOffset][, encoding])
buf.indexOf(value[, byteOffset][, encoding])

示例:
const buf = Buffer.from('this is a buffer');
console.log(buf.includes('this'));  // true
console.log(buf.indexOf('this'));  // 0

写入读取数值

写入方法:
位数固定且超过1个字节的: write{Double| Float | Int16 | Int32| UInt16 | UInt32 }{BE|LE}(value, offset)
位数不固定的: write{Int | UInt}{BE | LE}(value, offset, bytelength) //此方法提供了更灵活的位数表示数据(比如3位、5位)
位数固定是1个字节的: write{Int8 | Unit8}(value, offset)
读取方法:
位数固定且超过1个字节的: read{Double| Float | Int16 | Int32 | UInt16 | UInt32 }{BE|LE}(offset)
位数不固定的: read{Int | UInt}{BE | LE}(offset, byteLength)
位数固定是1个字节的: read{Int8 | Unit8}(offset)

合并Buffer.concat

Buffer释放

我们无法手动对buffer实例进行GC,只能依靠V8来进行,我们唯一能做的就是解除对buffer实例的引用

参考资料
https://juejin.im/post/5afd57e851882542ac7d76af#heading-17
http://nodejs.cn/api/buffer.html#buffer_buf_compare_target_targetstart_targetend_sourcestart_sourceend

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值