三W法则
Buffer是什么?Buffer什么时候用?Buffer怎么用?
官方回答
JavaScript 语言没有读取或操作二进制数据流的机制。 Buffer 类被引入作为 Node.js API 的一部分,使其可以在 TCP** 流或文件**系统操作等场景中处理二进制数据流。
Buffer是在内存中开辟的一片区域,用于存放二进制数据。Buffer所开辟的是堆外内存。
引申:
堆外内存(off-heap memory)
数据流(Stream)
堆外内存就是把内存对象分配在虚拟机的堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机),这样做的结果就是能够在一定程度上减少垃圾回收对应用程序造成的影响。
1,计算机将字符存储成二进制的方式,计算机会将无论图片、视频或其他数据都转换为二进制并存储,这就二进制数据。
在Node.js中,流(stream)就是一系列从A点到B点移动的数据,当你有一个很大的数据需要传输、搬运、处理时,你不需要等待所有数据都传输完成才开始下一步工作。
大量的数据会被分割成小块(chunks)进行传输,如果处理数据的时间比到达的时间快,这一时刻仅仅到达了一小部分数据,那这小部分数据需要等待剩下的数据填满,然后再送过去统一处理。这个"等待区域"就是buffer(缓冲池)
现实中:线看视频
网络足够快,数据流(stream)就可以足够快,可以让buffer迅速填满然后发送和处理,然后处理另一个,再发送,再另一个,再发送,然后整个stream完成。
当你网络连接很慢,当处理完当前的数据后,你的播放器就会暂停,或出现"缓冲"(buffer)字样,意思是正在收集更多的数据,或者等待更多的数据到来,才能下一步处理。当buffer装满并处理好,播放器就会显示数据,也就是播放视频了。在播放当前内容的时候,更多的数据也会源源不断的传输、到达和在buffer等待。
总结:Buffer类被引入到Node.js的API中,让其可以和二进制数据流交互和操作。
流的数据不能一次性获取到,数据也不会全部load到内存中,因此流非常适合大数据处理以及断断续续返回chunk的外部源。流的生产者与消费者之间的速度通常是不一致的,因此需要buffer来暂存一些数据。buffer大小通过highWaterMark参数指定,默认情况下是16Kb。
Buffer 对象占用的内存空间是不计算在 Node.js 进程内存空间限制上的,所以可以用来存储大对象,但是对象的大小还是有限制的。一般情况下32位系统大约是1G,64位系统大约是2G。
总结:
1,流的处理(不能一次性获取到)
2,存储的数据需要占用很大内存时
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.from不支持传入数字
Buffer.alloc、Buffer.allocUnsafe、Buffer.allocUnsafeSlow
1、Buffer.alloc**(size[, fill[, encoding]])**
0
。'utf8'
。const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64');
console.log(buf);
// 打印:
const buf = Buffer.alloc(5);
console.log(buf);
// 打印:
优点:确保新创建的 Buffer
实例的内容永远不会包含来自先前分配的敏感数据
缺点:调用 Buffer.alloc()
可能比替代的 Buffer.allocUnsafe()
慢得多(以零初始化)
2、Buffer.allocUnsafe(size)
const buf = Buffer.allocUnsafe(10);
console.log(buf);
// 打印(内容可能有所不同):
buf.fill(0);
console.log(buf);
// 打印:
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);
}
});
Buffer
切割出来对比:
1,Buffer.alloc会用0值填充已分配的内存,所以相比后两者速度上要慢,但是也较为安全
2,当分配的空间小于4KB的时候,allocUnsafe会直接从之前预分配的Buffer里面slice空间,因此速度allocUnsafe比allocUnsafeSlow要快,当大于等于4KB的时候二者速度相差无异。
const buf = Buffer.from('buffer');
console.log(buf.toString('utf8')); // buffer
console.log(buf.toString('utf8', 0, 2)); // bu
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
console.log(buf.toJSON()); // { type: 'Buffer', data: [ 1, 2, 3, 4, 5 ] }
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
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(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])
对比 buf 与 target,并返回一个数值,表明 buf 在排序上是否排在 target 前面、或后面、或相同。 对比是基于各自 Buffer 实际的字节序列。
主要的作用是用于对数组内的buffer实例排序
如果 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(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实例进行GC,只能依靠V8来进行,我们唯一能做的就是解除对buffer实例的引用
参考资料
https://juejin.im/post/5afd57e851882542ac7d76af#heading-17
http://nodejs.cn/api/buffer.html#buffer_buf_compare_target_targetstart_targetend_sourcestart_sourceend