水落石穿
1: Buffer 为什么不用 require来引入;
Buffer 是node的全局对象;
深入一下Buffer 的性能是由c++ 实现,非性能是有javaScript实现的;
Buffer 的内存不是由V8引擎分配的,他在V8所在内存的外面;启动node的时候,会把Buffer 挂载在全局对象上(global);
2: Buffer 的前世今生:
Buffer 继承 UintArray 继承 TypedArray
let buff = new Buffer(8); buff instanceof Buffer //true buff instanceof Uint8Array //true buff instanceof TypedArray //Error TypedArray is not defined //TypedArray 并没有在全局对象中, Uint8Array 的原型函数指向它所创建的实例 console.log(typeof Uint8Array.prototype) //"object" console.log(buff instanceof Uint8Array.prototype.constructor) //true
3: Buffer 元素的最大值:
let buff = new Buffer(1);
buff[0] 所占的内存为1个字节8位,最大值为255
buff[0] = 256;
console.log(buff[0]) //0 至于为什么等于0 属于2进制的内容了;
4: Buffer 的对象的储存位置:
let buff = new Buffer(1); //buff 所指向的地址 并不在V8的内存中,而是在V8之外的内存中, Node 的 C++ 层面实现内存的申请;
申请内存后,分配权在js的手里;
5: node 的slab内存分配策略;
slab有三个状态:
full:完全分配状态 partial:部分分配状态 empty:未被分配状态
在创建buffer对象的时候,会根据大小是否超过8kb,在node的c++层次申请不同的内存;分为大对象,小对象;
超过8kb 使用大对象,不超过使用小对象
在分配小对象内存过程中后又一个中间局部变量来参与资源的分配:pool;
let pool; Buffer.poolSize = 1024*8;//长度8kb function allocPool() { pool = new SlowBuffer(Buffer.poolSize); pool.used = 0; }; //此时,slab 处于 empty 状态。 let buf = new Buffer(12);//length=12; // 如果当前poll为undefined 或者 poll 剩下的内存不足以储存buf对象,就会创建一个新的poll对象 if(!pool || pool.length- pool.used < buf.length){ allocPool(); } //同时当前 Buffer 对象的 parent 属性指向该 slab,并记录下是从这个 slab 的哪个位置(offset)开始使用的,slab 对象自身也记录被使用了多少字节: this.parent = pool; this.offset = pool.used; pool.used += this.length; if (pool.used & 7) pool.used = (pool.used + 8) & ~7;此时的sold 的状态为partial;
当长度大于8kb的时候,我们就会申请一个大对象
let pool; function allocPool(length) { pool = new SlowBuffer(length); pool.used = length; }; //当Buffer对象的长度大于1024*8的时候,该对象单独占用一个内存块; let buff = new BUff(1024*9) if(buff.length > 1024*8){ buff.parent = new SlowBuffer(this.length); buff.offset = 0; }6: 关于内存的销毁:
这里主要说的是小内存的销毁,小内存里面保存着至少1个Buffder对象,当该内存中所有的内存没有被引用的时候,释放该内存;
也就是会,释放内存是整体释放,只要其中一个内存被引用,那么其他不再被引用的内存还是存在的;当那个唯一被引用的内存不再引用的时候,整体释放整个小内存块;
参考: Node.js 中的 Buffer 对象及其内存分配