1。 Buffer 只是查看ArrayBuffer 的视图。
一个Buffer,其实就是一个FastBuffer,其中extends(继承自)Uint8Array,是一个八位字节单元view(“partial accessor”)实际内存,ArrayBuffer。
?/lib/buffer.js#L65-L73 Node.js 9.4.0class FastBuffer extends Uint8Array {
constructor(arg1, arg2, arg3) {
super(arg1, arg2, arg3);
}
}
FastBuffer.prototype.constructor = Buffer;
internalBuffer.FastBuffer = FastBuffer;
Buffer.prototype = FastBuffer.prototype;
2。 ArrayBuffer 的大小及其视图的大小可能会有所不同。
原因 #1:Buffer.from(arrayBuffer[, byteOffset[, length]])。
使用Buffer.from(arrayBuffer[, byteOffset[, length]]),您可以创建Buffer,并指定其底层ArrayBuffer 以及视图的位置和大小。
const test_buffer = Buffer.from(new ArrayBuffer(50), 40, 10);
console.info(test_buffer.buffer.byteLength); // 50; the size of the memory.
console.info(test_buffer.length); // 10; the size of the view.
原因 #2:FastBuffer 的内存分配。
它根据大小以两种不同的方式分配内存。
-
如果大小小于 内存池大小的一半并且不为 0(“小”):它使用 内存池 准备所需的内存。
-
Else:它会创建一个专用的
ArrayBuffer,完全适合所需的内存。
?/lib/buffer.js#L306-L320 Node.js 9.4.0function allocate(size) {
if (size <= 0) {
return new FastBuffer();
}
if (size < (Buffer.poolSize >>> 1)) {
if (size > (poolSize - poolOffset))
createPool();
var b = new FastBuffer(allocPool, poolOffset, size);
poolOffset += size;
alignPool();
return b;
} else {
return createUnsafeBuffer(size);
}
}
?/lib/buffer.js#L98-L100 Node.js 9.4.0function createUnsafeBuffer(size) {
return new FastBuffer(createUnsafeArrayBuffer(size));
}
“内存池”是什么意思?
memory pool 是一个固定大小的预分配内存块,用于为Buffers 保留小尺寸内存块。使用它可以将小内存块紧密地结合在一起,从而防止fragmentation 由小内存块的单独管理(分配和释放)引起。
在这种情况下,内存池为ArrayBuffers,默认大小为8 KiB,在Buffer.poolSize中指定。当它为Buffer 提供一个小内存块时,它会检查最后一个内存池是否有足够的可用内存来处理这个问题;如果是,它会创建一个Buffer,“查看”内存池的给定部分块,否则,它会创建一个新的内存池,依此类推。
您可以访问Buffer 的底层ArrayBuffer。 Buffer 的buffer property(即继承自Uint8Array)持有它。 一个“小”Buffer 的buffer 属性是一个代表整个内存池的ArrayBuffer。所以在这种情况下,ArrayBuffer 和Buffer 大小不一。
const zero_sized_buffer = Buffer.allocUnsafe(0);
const small_buffer = Buffer.from([0xC0, 0xFF, 0xEE]);
const big_buffer = Buffer.allocUnsafe(Buffer.poolSize >>> 1);
// A `Buffer`'s `length` property holds the size, in octets, of the view.
// An `ArrayBuffer`'s `byteLength` property holds the size, in octets, of its data.
console.info(zero_sized_buffer.length); /// 0; the view's size.
console.info(zero_sized_buffer.buffer.byteLength); /// 0; the memory..'s size.
console.info(Buffer.poolSize); /// 8192; a memory pool's size.
console.info(small_buffer.length); /// 3; the view's size.
console.info(small_buffer.buffer.byteLength); /// 8192; the memory pool's size.
console.info(Buffer.poolSize); /// 8192; a memory pool's size.
console.info(big_buffer.length); /// 4096; the view's size.
console.info(big_buffer.buffer.byteLength); /// 4096; the memory's size.
console.info(Buffer.poolSize); /// 8192; a memory pool's size.
3。所以我们需要提取它“views”的内存。
ArrayBuffer 的大小是固定的,因此我们需要通过复制该部分来将其提取出来。为此,我们使用Buffer 的byteOffset property 和length property,它们继承自Uint8Array 和the ArrayBuffer.prototype.slice method,它复制了ArrayBuffer 的一部分。这里的slice()-ing 方法的灵感来自@ZachB。
const test_buffer = Buffer.from(new ArrayBuffer(10));
const zero_sized_buffer = Buffer.allocUnsafe(0);
const small_buffer = Buffer.from([0xC0, 0xFF, 0xEE]);
const big_buffer = Buffer.allocUnsafe(Buffer.poolSize >>> 1);
function extract_arraybuffer(buf)
{
// You may use the `byteLength` property instead of the `length` one.
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.length);
}
// A copy -
const test_arraybuffer = extract_arraybuffer(test_buffer); // of the memory.
const zero_sized_arraybuffer = extract_arraybuffer(zero_sized_buffer); // of the... void.
const small_arraybuffer = extract_arraybuffer(small_buffer); // of the part of the memory.
const big_arraybuffer = extract_arraybuffer(big_buffer); // of the memory.
console.info(test_arraybuffer.byteLength); // 10
console.info(zero_sized_arraybuffer.byteLength); // 0
console.info(small_arraybuffer.byteLength); // 3
console.info(big_arraybuffer.byteLength); // 4096
4。性能提升
如果要将结果作为只读使用,或者修改输入Buffers'的内容也可以,可以避免不必要的内存复制。
const test_buffer = Buffer.from(new ArrayBuffer(10));
const zero_sized_buffer = Buffer.allocUnsafe(0);
const small_buffer = Buffer.from([0xC0, 0xFF, 0xEE]);
const big_buffer = Buffer.allocUnsafe(Buffer.poolSize >>> 1);
function obtain_arraybuffer(buf)
{
if(buf.length === buf.buffer.byteLength)
{
return buf.buffer;
} // else:
// You may use the `byteLength` property instead of the `length` one.
return buf.subarray(0, buf.length);
}
// Its underlying `ArrayBuffer`.
const test_arraybuffer = obtain_arraybuffer(test_buffer);
// Just a zero-sized `ArrayBuffer`.
const zero_sized_arraybuffer = obtain_arraybuffer(zero_sized_buffer);
// A copy of the part of the memory.
const small_arraybuffer = obtain_arraybuffer(small_buffer);
// Its underlying `ArrayBuffer`.
const big_arraybuffer = obtain_arraybuffer(big_buffer);
console.info(test_arraybuffer.byteLength); // 10
console.info(zero_sized_arraybuffer.byteLength); // 0
console.info(small_arraybuffer.byteLength); // 3
console.info(big_arraybuffer.byteLength); // 4096