内容简介:在node 8.0之前,buffer主要通过构造函数使用new Buffer(...)的方式来创建。因为根据参数的不同,生成buffer的行为和结果都大不相同,所以从node 8.0版本开始,buffer的创建被拆分到几个不同的方法(from, alloc, allocUnsafe)。在看详细的方法之前,需要知道的是,node为了节约系统资源提高效率,先预申请了一部分内存作为buffer pool。 如果申请的buffer的大小比较小的情况下,会优先从这个pool里面进行分配。 这部分操作,都放到了allo
在node 8.0之前,buffer主要通过构造函数使用new Buffer(...)的方式来创建。因为根据参数的不同,生成buffer的行为和结果都大不相同,所以从node 8.0版本开始,buffer的创建被拆分到几个不同的方法(from, alloc, allocUnsafe)。
在看详细的方法之前,需要知道的是,node为了节约系统资源提高效率,先预申请了一部分内存作为buffer pool。 如果申请的buffer的大小比较小的情况下,会优先从这个pool里面进行分配。 这部分操作,都放到了allocate函数里面:
function allocate(size) { if (size <= 0) { return new FastBuffer(); // 没size,直接返回一个空buffer } if (size < (Buffer.poolSize >>> 1)) { // size小于pool的一半(4kb) if (size > (poolSize - poolOffset)) // size比pool剩下的空间大的话,就再申请一个pool createPool(); const b = new FastBuffer(allocPool, poolOffset, size); poolOffset += size; alignPool(); return b; } return createUnsafeBuffer(size); // 如果size比较大, 则直接申请一个未初始化的buffer } 复制代码
Buffer.from
Buffer.from比较复杂,根据入参大致有3种不同的处理方式:
Buffer.from(string, encoding)
当传入的第一个参数为一个字符串时,会调用对应的fromString方法,代码如下:
function fromString(string, encoding) { // 确定string在encode后的大小,如果string为0,则返回一个空buffer let length; if (typeof encoding !== 'string' || encoding.length === 0) { if (string.length === 0) return new FastBuffer(); encoding = 'utf8'; length = byteLengthUtf8(string); } else { length = byteLength(string, encoding, true); if (length === -1) throw new ERR_UNKNOWN_ENCODING(encoding); if (string.length === 0) return new FastBuffer(); } // 如果大小大于poolSize的一半(4kb), 直接分配内存。 if (length >= (Buffer.poolSize >>> 1)) return createFromString(string, encoding); // 如果大小大于pool里剩下的内存,则新建一个pool if (length > (poolSize - poolOffset)) createPool(); let b = new FastBuffer(allocPool, poolOffset, length); const actual = b.write(string, encoding); if (actual !== length) { b = new FastBuffer(allocPool, poolOffset, actual); } poolOffset += actual; alignPool(); return b; } 复制代码
c++扩展直接分配内存:
void CreateFromString(const FunctionCallbackInfo<Value>& args) { CHECK(args[0]->IsString()); CHECK(args[1]->IsString()); enum encoding enc = ParseEncoding(args.GetIsolate(), args[1].As<String>(), UTF8); Local<Object> buf; if (New(args.GetIsolate(), args[0].As<String>(), enc).ToLocal(&buf)) args.GetReturnValue().Set(buf); } 复制代码
总结下来就是3个步骤:
- 通过encode的方式,确定最终需要的buffer大小
- 如果是个大buffer(>4kb),那就不走buffer pool了,直接通过c++拓展分配内存。
- 小buffer的话,看看上一个pool剩下的内存还够不够,够的话继续用,不够了就再新建一个内存来分配。
Buffer.from(arrayBuffer)
function fromArrayBuffer(obj, byteOffset, length) { ... byteOffset校对逻辑 return new FastBuffer(obj, byteOffset, length); } 复制代码
如果传入的是一个arrayBuffer, 那就直接生成一个FastBuffer对象,而FastBuffer直接继承了Uint8Array,根据typedArray的定义,如果传入一个ArrayBuffer,那就返回 一个新的TypedArray来共享这段内存
When called with a buffer, and optionally a byteOffset and a length argument, a new typed array view is created that views the specified ArrayBuffer. The byteOffset and length parameters specify the memory range that will be exposed by the typed array view. If both are omitted, all of buffer is viewed; if only length is omitted, the remainder of buffer is viewed.
Buffer.from(array/buffer/typedArray)
function fromObject(obj) { if (isUint8Array(obj)) { const b = allocate(obj.length); // // 从pool里分配或者直接分配 if (b.length === 0) return b; _copy(obj, b, 0, 0, obj.length); // // 把数据拷贝到新分配的内存上。 return b; } if (obj.length !== undefined || isAnyArrayBuffer(obj.buffer)) { if (typeof obj.length !== 'number') { return new FastBuffer(); } return fromArrayLike(obj); } if (obj.type === 'Buffer' && Array.isArray(obj.data)) { return fromArrayLike(obj.data); } } 复制代码
// 把类数组的数据(array/buffer)都写入新的buffer function fromArrayLike(obj) { const length = obj.length; const b = allocate(length); for (var i = 0; i < length; i++) b[i] = obj[i]; return b; } 复制代码
Buffer.alloc
Buffer.alloc(size, fill, encoding)接受3个参数,返回一个被初始化了的buffer:
Buffer.alloc = function alloc(size, fill, encoding) { assertSize(size); if (fill !== undefined && fill !== 0 && size > 0) { const buf = createUnsafeBuffer(size); return _fill(buf, fill, 0, buf.length, encoding); } return new FastBuffer(size); }; // 创建一个未初始化的buffer function createUnsafeBuffer(size) { zeroFill[0] = 0; try { return new FastBuffer(size); } finally { zeroFill[0] = 1; } } 复制代码
通过FastBuffer直接生成一个Uint8Array, 如果传了fill,则还要对生成的Uint8Array做一个填充操作。
Buffer.allocUnsafe
这个就简单了,直接调用allocate函数申请内存
Buffer.allocUnsafe = function allocUnsafe(size) { assertSize(size); return allocate(size); }; 复制代码
对应allocUnsafe还有一个allocUnsafeSlow函数,区别就是它直接申请了内存,没有走pool。
Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) { assertSize(size); return createUnsafeBuffer(size); }; 复制代码
参考文档
以上所述就是小编给大家介绍的《Node 学习小结 - Buffer生成策略》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ruby原理剖析
[美] Patrick Shaughnessy / 张汉东、秦凡鹏 / 华中科技大学出版社 / 2016-12-1 / 78.80元
《Ruby原理剖析》解开Ruby编程语言的魔法面纱。全书图文并茂、深入浅出地剖析了Ruby编程语言的核心工作原理。作者本着科学实证的精神,设计了一系列实验,帮助读者轻松了解这门编程语言的工作奥秘,包括Ruby如何用虚拟机执行代码,Ruby的垃圾回收算法,以及类和模块在Ruby内部的关系等。一起来看看 《Ruby原理剖析》 这本书的介绍吧!