Open EasonYou opened 4 years ago
在Node应用中,需要处理网络协议、数据库操作、图片处理、接受上传文件等等,在网络流与文件的操作中,还需要处理大量的二进制文件。自有的字符串远远不能满足这些需求,于是Buffer应运而生
Buffer是二进制数据,字符串与Buffer存在编码关系
Buffer是一个类Array的对象,但主要应用于操作字节。
性能方面由C++实现,非性能相关用JavaScript实现。
Buffer所占用的内存不是通过V8分配,属于堆外内存。这是由于V8的垃圾回收性能影响,将常用的操作对象用更高效和专有的内存分配回收策略来管理。
从Node 6.0.0开始,抛弃new Buffer,由from方法创建对应的buffer,它的元素是16进制的两位数,即0到255的数值
new Buffer
from
const str = '深入浅出node.js'; const buf = Buffer.from(str, 'utf-8')
可以通过alloc方法进行内存申请。如果填充内容为undefined,用0填充
alloc
undefined
const buf = Buffer.alloc(100)
可以通过下标对其进行赋值
const buf = Buffer.alloc(100) buf[1] = 5 console.log(buf[1]) // 5 buf[2] = 300 console.log(buf[2]) // 44 buf[3] = 3.1415 console.log(buf[3]) // 3 buf[4] = -100 console.log(buf[4])
如上,给元素的赋值如果小于0,就将该值逐次加256,直到得到一个0-255之间的整数。如果得到的数大于255,则逐次减256,直到得到一个0-255之间的整数,如果是小数,只保留整数部分
Buffer的内存分配不在V8的堆内存中,而是在Node的C++层面实现内存而申请的。
因为处理大量的字节数据,不能采用需要一点内存就向操作系统申请一点内存的方式,这可能会造成大量的内存申请的系统调用,对操作系统有一定压力。
所以,Node是在C++层面申请内存,在JavaScript中分配内存的策略
slab是一种动态内存管理机制,简而言之,就是一块申请好的固定大小的内存区域
它具有以下三种状态
Node以8kb为界限来区分Buffer是大对象还是小对象,这个值,在Buffer.poolSize中设置。这个值就是每个slab的大小值。在JavaScrip层面以它为单位单元进行内存的分配
Buffer.poolSize
申请内存的时候,会直接初始化一个8kb的内存空间,同时初始化一个变量poolOffset来记录这个内存空间已经使用了多少字节
poolOffset
接着如果分配一个小于8k(如2048)的Buffer对象,则会在此slab内存中去分配,此时poolOffset会记录2048
如果此时分配一个比6144字节还大的buffer时,原来的slab剩余空间不够则会构造一个新的slab。这样的话就会造成slab的空间浪费
同时,只有在slab中的小Buffer对象都在作用于释放时,slab的8KB空间才会被回收
allocUnsafeSlow
不推荐用allocUnsafeSlow进行分配,用Buffer.alloc代替
Buffer.alloc
以上提到的Buffer对象都是JavaScript层面的,能够被V8的垃圾回收标记回收。但是其内部的parent属性指向的SlowBuffer对象来自Node自身的C++中的定制,是C++层面上的Buffer对象,所以内存不在V8的堆中
以上是在深入浅出Node.js中描述的分配机制,在最新的node实现中,poolSize依然是8KB,但是分配的大小对象会以4KB作为判断的区分
深入浅出Node.js
poolSize
真正的内存是在Node的C++层面提供,JavaScript层面只是使用它
小而频繁的Buffer操作,用slab机制进行预先申请和事后分配,使得JavaScript到操作系统之间不必有过多的内存申请方面的系统调用
大的Buffer而言,直接使用C++层面提供的内存
分配一个大小为size 字节的新 Buffer。 如果fill为undefined,则用零填充Buffer。
size
Buffer
fill
如果size大于 buffer.constants.MAX_LENGTH(在 32 位的架构上,该值是 (2^30)-1 (~1GB)。 在 64 位的架构上,该值是 (2^31)-1 (~2GB)) 或小于 0,则抛出 ERR_INVALID_OPT_VALUE。 如果size为 0,则创建一个零长度的Buffer。
buffer.constants.MAX_LENGTH
ERR_INVALID_OPT_VALUE
const buf = Buffer.alloc(5); console.log(buf); // 打印: <Buffer 00 00 00 00 00> const buf = Buffer.alloc(5, 'a'); console.log(buf); // 打印: <Buffer 61 61 61 61 61>
如果指定了encoding会先做decode,再转为buffer
encoding
const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64'); console.log(buf.toString()); // hello world
创建一个大小为 size 字节的新 Buffer。实例的底层内存是未初始化的,新创建的Buffer的内容是未知的,可能会包含敏感数据。但是分配内存小于4KB时,则会从一个预分配的Buffer切割出来。可避免垃圾回收机制因创建太多独立的Buffer而过度使用(这里指的就是上面的slab分配机制)。
同allocUnsafe,但是直接申请一个大内存的空间存放,而不走slab的分配机制
allocUnsafe
Buffer.from(array)
Buffer.from(arrayBuffer[, byteOffset[, length]])
Buffer.from(buffer)
Buffer.from(string[, encoding])
这是用于缓冲池的预分配的内部 Buffer 实例的大小(以字节为单位)。 该值可以修改。
buffer中很多方法与Array重叠,可直接查阅Array的文档。
在引入TypedArray前,JavaScript语言没有用于读取或操作二进制数据流的机制。因此产生了Buffer类作为Node API的一部分,用于TCP流、文件系统操作、以及其他上下文中与八位字节流进行交互。
它与TypedArray一样,都是描述了一个底层的二进制数据缓冲区(binary data buffer),也就是Array Buffer的一个类数组视图。因为JavaScript不能直接操作二进制数据,需要通过视图,来进行二进制数据的操作。
二进制数据缓冲区
Array Buffer
实际上Buffer就是TypedArray视图中的Unit8Array的node实现
TypedArray
Unit8Array
通过使用 TypedArray 对象的 .buffer 属性,可以创建一个与 TypedArray 实例共享相同内存的新 Buffer。
const arr = new Uint16Array(2); arr[0] = 5000; arr[1] = 4000; // 拷贝 `arr` 的内容。 const buf1 = Buffer.from(arr); // 与 `arr` 共享内存。 const buf2 = Buffer.from(arr.buffer); console.log(buf1); // 打印: <Buffer 88 a0> console.log(buf2); // 打印: <Buffer 88 13 a0 0f> arr[1] = 6000; console.log(buf1); // 打印: <Buffer 88 a0> console.log(buf2); // 打印: <Buffer 88 13 70 17>
当使用 TypedArray 的 .buffer 创建 Buffer 时,也可以通过传入 byteOffset 和 length 参数只使用 TypedArray 的一部分。
const arr = new Uint16Array(20); // 依然共享TypedArray的内存,裁切16个长度 const buf = Buffer.from(arr.buffer, 0, 16); console.log(buf.length); // 打印: 16
ArrayBuffer对象代表原始的二进制数据,TypedArray视图用来读写简单类型的二进制数据,DataView视图用来读写复杂类型的二进制数据。
// 下面代码是语法格式,不能直接运行, // TypedArray 关键字需要替换为底部列出的构造函数。 new TypedArray(); // ES2017中新增 new TypedArray(length); new TypedArray(typedArray); new TypedArray(object); new TypedArray(buffer [, byteOffset [, length]]); // TypedArray 指的是以下的其中之一: // 这些方法都会生成二进制数据,只是视图的形式不一样 Int8Array(); // 8 位二进制有符号整数 Uint8Array(); // 8 位无符号整数(超出范围后从另一边界循环) Uint8ClampedArray(); // 8 位无符号整数(超出范围后为边界值) Int16Array(); // 16 位二进制有符号整数 Uint16Array(); // 16 位无符号整数 Int32Array(); // 32 位二进制有符号整数 Uint32Array(); // 32 位无符号整数 Float32Array(); // 32 位 IEEE 浮点数(7 位有效数字,如 1.1234567) Float64Array(); // 64 位 IEEE 浮点数(16 有效数字,如 1.123...15)
Buffer的构造函数已经不建议使用,可以直接用from方法alloc方法或者allocUnsafe等等。
// 构造函数,已经弃用,但是还存在,依然可用,会有warning function Buffer(arg, encodingOrOffset, length) { // Common case. if (typeof arg === 'number') { // ... return Buffer.alloc(arg); } return Buffer.from(arg, encodingOrOffset, length); }
用于分配一个全新的Buffer,如果fill为空,用0填充
Buffer.alloc = function alloc(size, fill, encoding) { assertSize(size); // 判断size是不是为数字,不是则抛错 // 如果fill有内容 if (fill !== undefined && fill !== 0 && size > 0) { const buf = createUnsafeBuffer(size); // 先分配一个size大小的buffer return _fill(buf, fill, 0, buf.length, encoding); // 然后把内容填充进去 } // fill无内容的情况,直接用FasterBuffer分配 return new FastBuffer(size); };
fill不为空那么,其中一个关键方法则是createUnsafeBuffer
createUnsafeBuffer
其实,这个方法也是allocUnsafeSlow调用的方法
Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) { assertSize(size); // 判断size是不是为数字,不是则抛错 return createUnsafeBuffer(size); // 创建一个大小为 size 字节的新 Buffer }; // const zeroFill = bindingZeroFill || [0]; // 创建一个大小为 size 字节的新 Buffer function createUnsafeBuffer(size) { // .. try { return new FastBuffer(size); //分配一个size大小的内存 } finally { /** ... */ } }
可以看到,分配空的buffer,都是通过FastBuffer做填充
FastBuffer
// FastBuffer直接继承了Uint8Array // 从这里可以解释,Buffer就是Unit8Array的一个改良版本,更适用于Node.js class FastBuffer extends Uint8Array {} FastBuffer.prototype.constructor = Buffer; Buffer.prototype = FastBuffer.prototype; //Buffer继承了FastBuffer的方法 // addBufferPrototypeMethods是给原型链上添加了非常多的方法 addBufferPrototypeMethods(Buffer.prototype); function addBufferPrototypeMethods(proto) { // 这里只列出一个方法作为示例 proto.readBigUInt64LE = readBigUInt64LE, // ... }
到这里,可以知道,分配的底层逻辑,就是通过TypedArray中的Uint8Array来实现的
Uint8Array
那么,alloc方法会比allocUnsafeSlow更安全的原因,就在于fill方法中
// _fill方法也是fill下调用的方法 Buffer.prototype.fill = function fill(value, offset, end, encoding) { return _fill(this, value, offset, end, encoding); // fill中调用_fill方法 }; function _fill(buf, value, offset, end, encoding) { // 如果是字符串类型,则需要对其进行encoding处理 if (typeof value === 'string') { // ... // 这里只给出重要的点,则是value为空的话,value的填充也用0去做填充 if (value.length === 0) { // If value === '' default to zero. value = 0; } // ... } else { // 否则,把encoding设为空 encoding = undefined; } // 如果没填写偏移量,则默认为0,结束偏移量为最后的长度 if (offset === undefined) { offset = 0; end = buf.length; } else { // 判断offset是否合法,非法则报错 validateInt32(offset, 'offset', 0); // Invalid ranges are not set to a default, so can range check early. if (end === undefined) { end = buf.length; } else { // 判断结束偏移量是否合法,非法则报错 validateInt32(end, 'end', 0, buf.length); } // 如果偏移量大于结束偏移量,直接返回Buffer实例 if (offset >= end) return buf; } // 填充buffer,bindingFii是C++模块的buffer实现 // res是成功标识,0为成功 const res = bindingFill(buf, value, offset, end, encoding); // ... return buf; }
至此,alloc和allocUnsafeSlow以及fill的方法到此完毕,总结下各自的作用
与alloc方法不同,from方法是直接把value转化成Buffer
Buffer.from = function from(value, encodingOrOffset, length) { // 如果是字符串,带上encoding直接做转换 if (typeof value === 'string') return fromString(value, encodingOrOffset); // 如果是对象 if (typeof value === 'object' && value !== null) { // 如果是ArrayBuffer,带上offset和拷贝字节数做转换 if (isAnyArrayBuffer(value)) return fromArrayBuffer(value, encodingOrOffset, length); // 为了保证其值的正确性,需要判断valueOf的值而不是原始值 const valueOf = value.valueOf && value.valueOf(); if (valueOf != null && // 如果其valueOf的值不等于原始值,则将其valueOf递归from来做buffer转换 valueOf !== value && (typeof valueOf === 'string' || typeof valueOf === 'object')) { return from(valueOf, encodingOrOffset, length); } // 转换Object const b = fromObject(value); if (b) return b; // 兼容primitive为string的情况 if (typeof value[SymbolToPrimitive] === 'function') { const primitive = value[SymbolToPrimitive]('string'); if (typeof primitive === 'string') { return fromString(primitive, encodingOrOffset); } } } // ... };
from方法兼容了string、Object和ArrayBuffer等情况,接下来分别阐述其对应方法
fromString先是做了encoding的前置检查动作以及encoding的参数设置,通过fromStringFast进行转换
fromString
fromStringFast
在这里,做了一个buffer的内存分配中,非常重要的一个点,就是上面提及的slab机制
slab
function fromString(string, encoding) { let ops; // encoding无效的情况 if (typeof encoding !== 'string' || encoding.length === 0) { if (string.length === 0) // 如果string同样为空,分配一个空的buffer return new FastBuffer(); // 否则,默认encoding为utf8 ops = encodingOps.utf8; encoding = undefined; } else { // 获取encoding的参数 ops = getEncodingOps(encoding); // ... } // 通过fromStringFast来转换 return fromStringFast(string, ops); } Buffer.poolSize = 8 * 1024; // poolSize是单个内存块的大小,默认为8KB // slab机制在这里实现 // 用于创建一个8KB的内存块 function createPool() { poolSize = Buffer.poolSize; allocPool = createUnsafeBuffer(poolSize).buffer; poolOffset = 0; } function fromStringFast(string, ops) { // 由于encoding的不同,需要特殊地获取确切的字符串长度 const length = ops.byteLength(string); // 如果长度大于4096(>>>是二进制右移,即/2) if (length >= (Buffer.poolSize >>> 1)) // 直接在当前分配可用的内存池去分配 return createFromString(string, ops.encodingVal); // 如果长度大于剩余的内存块大小,则分配一个新的内存块 if (length > (poolSize - poolOffset)) createPool(); // 通过上面的内存池分配记录,来申请获取对应的内存块 let b = new FastBuffer(allocPool, poolOffset, length); // 往对应的内存,写入内容 const actual = ops.write(b, string, 0, length); // 兜底内存块不够的情况,但此情况较少见 if (actual !== length) { // byteLength() may overestimate. That's a rare case, though. b = new FastBuffer(allocPool, poolOffset, actual); } // 更新偏移量 poolOffset += actual; // ... return b; }
因为ArrayBuffer可以通过Uint8Array直接分配,也没有encoding的限制,所以此方法只做了区间的校验,然后直接通过FastBuffer(Uint8Array)去做buffer转换
FastBuffer(Uint8Array)
function fromArrayBuffer(obj, byteOffset, length) { // ... // 想要获取的buffer区间小于零的话,报错 const maxLength = obj.byteLength - byteOffset; if (maxLength < 0) throw new ERR_BUFFER_OUT_OF_BOUNDS('offset'); // ... return new FastBuffer(obj, byteOffset, length); }
Object分为Uint8Array、Object、ArrayLike三种
Object
ArrayLike
// 先看allocate方法 function allocate(size) { // 空的话,直接分配空的buffer if (size <= 0) { return new FastBuffer(); } // 通string,分配小内存buffer用slab机制控制 if (size < (Buffer.poolSize >>> 1)) { if (size > (poolSize - poolOffset)) createPool(); const b = new FastBuffer(allocPool, poolOffset, size); poolOffset += size; alignPool(); return b; } // 否则,直接分配大内存 return createUnsafeBuffer(size); } function fromObject(obj) { // 如果是Uint8Array的数据格式的话 if (isUint8Array(obj)) { // 先分配内存 const b = allocate(obj.length); // 分配长度为空的话,直接返回 if (b.length === 0) return b; // 虽然这个方法名是copy,实质上是做了b.set(obj, start)的动作 _copy(obj, b, 0, 0, obj.length); return b; } // 如果是数组 if (obj.length !== undefined || isAnyArrayBuffer(obj.buffer)) { // 如果length被各种改些,则是直接分配空的buffer if (typeof obj.length !== 'number') { return new FastBuffer(); } // 正确的ArrayLike分配 return fromArrayLike(obj); } // .. } function _copy(source, target, targetStart, sourceStart, sourceEnd) { // 上面都是在判断数据的正确性,然后直接调用TypedArray.prototype.set方法 // set() 方法用于从指定数组中读取值,并将其存储在类型化数组中。 target.set(source, targetStart); return nb; } function fromArrayLike(obj) { const length = obj.length; // 分配内存 const b = allocate(length); // 绑定内容 for (let i = 0; i < length; i++) b[i] = obj[i]; return b; }
以上则是buffer的核心方法的源码阅读
Node.js的Buffer,可以说是对TypedArray的Uint8Array一个封装。
但是Buffer在此基础上,对string的encoding做了严格控制和长度的转换,在这基础上与Object类型用同样的slab内存分配机制,对大内存和小内存的分配做了特殊的管理,会预先分配8KB的内存池,大于4KB的数据直接分配需要的内存,小于4KB的数据,判断现有内存池是否有足够空间,有则插入无则重新分配8KB空间。通过这种方式来减少内存支出
Buffer(缓冲器)
在Node应用中,需要处理网络协议、数据库操作、图片处理、接受上传文件等等,在网络流与文件的操作中,还需要处理大量的二进制文件。自有的字符串远远不能满足这些需求,于是Buffer应运而生
Buffer是二进制数据,字符串与Buffer存在编码关系
Buffer结构
Buffer是一个类Array的对象,但主要应用于操作字节。
模块结构
性能方面由C++实现,非性能相关用JavaScript实现。
Buffer所占用的内存不是通过V8分配,属于堆外内存。这是由于V8的垃圾回收性能影响,将常用的操作对象用更高效和专有的内存分配回收策略来管理。
Buffer分配
从Node 6.0.0开始,抛弃
new Buffer
,由from
方法创建对应的buffer,它的元素是16进制的两位数,即0到255的数值可以通过
alloc
方法进行内存申请。如果填充内容为undefined
,用0填充可以通过下标对其进行赋值
如上,给元素的赋值如果小于0,就将该值逐次加256,直到得到一个0-255之间的整数。如果得到的数大于255,则逐次减256,直到得到一个0-255之间的整数,如果是小数,只保留整数部分
Buffer的内存分配
Buffer的内存分配不在V8的堆内存中,而是在Node的C++层面实现内存而申请的。
因为处理大量的字节数据,不能采用需要一点内存就向操作系统申请一点内存的方式,这可能会造成大量的内存申请的系统调用,对操作系统有一定压力。
所以,Node是在C++层面申请内存,在JavaScript中分配内存的策略
slab分配机制
slab是一种动态内存管理机制,简而言之,就是一块申请好的固定大小的内存区域
它具有以下三种状态
Node以8kb为界限来区分Buffer是大对象还是小对象,这个值,在
Buffer.poolSize
中设置。这个值就是每个slab的大小值。在JavaScrip层面以它为单位单元进行内存的分配申请内存的时候,会直接初始化一个8kb的内存空间,同时初始化一个变量
poolOffset
来记录这个内存空间已经使用了多少字节接着如果分配一个小于8k(如2048)的Buffer对象,则会在此slab内存中去分配,此时
poolOffset
会记录2048如果此时分配一个比6144字节还大的buffer时,原来的slab剩余空间不够则会构造一个新的slab。这样的话就会造成slab的空间浪费
同时,只有在slab中的小Buffer对象都在作用于释放时,slab的8KB空间才会被回收
allocUnsafeSlow
进行分配)对象作为slab单元,将会被这个大Buffer对象独占不推荐用
allocUnsafeSlow
进行分配,用Buffer.alloc
代替以上提到的Buffer对象都是JavaScript层面的,能够被V8的垃圾回收标记回收。但是其内部的parent属性指向的SlowBuffer对象来自Node自身的C++中的定制,是C++层面上的Buffer对象,所以内存不在V8的堆中
小结
真正的内存是在Node的C++层面提供,JavaScript层面只是使用它
小而频繁的Buffer操作,用slab机制进行预先申请和事后分配,使得JavaScript到操作系统之间不必有过多的内存申请方面的系统调用
大的Buffer而言,直接使用C++层面提供的内存
Buffer与性能
Buffer常用方法介绍
Buffer.alloc(size[, fill[, encoding]])
分配一个大小为
size
字节的新Buffer
。 如果fill
为undefined
,则用零填充Buffer
。如果
size
大于buffer.constants.MAX_LENGTH
(在 32 位的架构上,该值是 (2^30)-1 (~1GB)。 在 64 位的架构上,该值是 (2^31)-1 (~2GB)) 或小于 0,则抛出ERR_INVALID_OPT_VALUE
。 如果size
为 0,则创建一个零长度的Buffer
。如果指定了
encoding
会先做decode,再转为bufferBuffer.allocUnsafe(size)
创建一个大小为
size
字节的新Buffer
。实例的底层内存是未初始化的,新创建的Buffer的内容是未知的,可能会包含敏感数据。但是分配内存小于4KB时,则会从一个预分配的Buffer切割出来。可避免垃圾回收机制因创建太多独立的Buffer而过度使用(这里指的就是上面的slab分配机制)。Buffer.allocUnsafeSlow(size)
同
allocUnsafe
,但是直接申请一个大内存的空间存放,而不走slab的分配机制Buffer.from
Buffer.from(array)
使用八位字节数组 array 分配一个新的 BufferBuffer.from(arrayBuffer[, byteOffset[, length]])
创建 ArrayBuffer 的视图,但不会拷贝底层内存。Buffer.from(buffer)
拷贝 buffer 的数据到新建的 Buffer 实例Buffer.from(string[, encoding])
创建一个包含 string 的新 Buffer。 encoding 参数指定 string 的字符编码。Buffer.poolSize
这是用于缓冲池的预分配的内部 Buffer 实例的大小(以字节为单位)。 该值可以修改。
其他与Array重叠的方法
buffer中很多方法与Array重叠,可直接查阅Array的文档。
Buffer与TypedArray、DataView
在引入TypedArray前,JavaScript语言没有用于读取或操作二进制数据流的机制。因此产生了Buffer类作为Node API的一部分,用于TCP流、文件系统操作、以及其他上下文中与八位字节流进行交互。
它与TypedArray一样,都是描述了一个底层的
二进制数据缓冲区
(binary data buffer),也就是Array Buffer
的一个类数组视图。因为JavaScript不能直接操作二进制数据,需要通过视图,来进行二进制数据的操作。实际上Buffer就是
TypedArray
视图中的Unit8Array
的node实现通过使用 TypedArray 对象的 .buffer 属性,可以创建一个与 TypedArray 实例共享相同内存的新 Buffer。
当使用 TypedArray 的 .buffer 创建 Buffer 时,也可以通过传入 byteOffset 和 length 参数只使用 TypedArray 的一部分。
ArrayBuffer对象代表原始的二进制数据,TypedArray视图用来读写简单类型的二进制数据,DataView视图用来读写复杂类型的二进制数据。
TypedArray语法
Buffer源码阅读
Buffer
的构造函数已经不建议使用,可以直接用from
方法alloc
方法或者allocUnsafe
等等。构造函数
alloc方法
用于分配一个全新的Buffer,如果fill为空,用0填充
allocUnsafeSlow/createUnsafeBuffer方法
fill不为空那么,其中一个关键方法则是
createUnsafeBuffer
其实,这个方法也是
allocUnsafeSlow
调用的方法FastBuffer
可以看到,分配空的buffer,都是通过
FastBuffer
做填充到这里,可以知道,分配的底层逻辑,就是通过
TypedArray
中的Uint8Array
来实现的那么,
alloc
方法会比allocUnsafeSlow
更安全的原因,就在于fill方法中buf.fill/_fill方法
总结
至此,
alloc
和allocUnsafeSlow
以及fill
的方法到此完毕,总结下各自的作用from方法
与
alloc
方法不同,from
方法是直接把value转化成Bufferalloc
的value只能是字符串/buffer/int,用作分配的预置填充from
的value是除了number的几乎所有类型,用于转换成bufferfrom
方法兼容了string、Object和ArrayBuffer等情况,接下来分别阐述其对应方法fromString
fromString
先是做了encoding的前置检查动作以及encoding的参数设置,通过fromStringFast
进行转换在这里,做了一个buffer的内存分配中,非常重要的一个点,就是上面提及的
slab
机制fromArrayBuffer
因为ArrayBuffer可以通过
Uint8Array
直接分配,也没有encoding的限制,所以此方法只做了区间的校验,然后直接通过FastBuffer(Uint8Array)
去做buffer转换fromObject
Object分为
Uint8Array
、Object
、ArrayLike
三种总结
以上则是buffer的核心方法的源码阅读
Node.js的Buffer,可以说是对TypedArray的
Uint8Array
一个封装。但是Buffer在此基础上,对string的encoding做了严格控制和长度的转换,在这基础上与Object类型用同样的slab内存分配机制,对大内存和小内存的分配做了特殊的管理,会预先分配8KB的内存池,大于4KB的数据直接分配需要的内存,小于4KB的数据,判断现有内存池是否有足够空间,有则插入无则重新分配8KB空间。通过这种方式来减少内存支出