2. Buffer内存分配

Buffer对象的内存分配不是在V8的堆内存中,而是在Node的C++层面实现内存的申请的。
因为处理大量的字节数据不能采用需要一点内存就向操作系统申请一点内存的方式,这可能造成大量的内存申请的系统调用,对操作系统有一定压力。为此Node在内存的使用上应用的是在C++层面申请内存、在JavaScript中分配内存的策略。

为了高效地使用申请来的内存,Node采用了slab分配机制。它是一种动态内存管理制度。

简单而言,slab就是一块申请好的固定大小的内存区域。slab具有如下三种状态。

  • full: 完全分配状态。
  • partial: 部分分配状态。
  • empty: 没有被分配状态 。

v6.0之前创建Buffer对象直接使用new Buffer()构造函数来创建对象实例,但是Buffer对内存的权限操作相比很大,可以直接捕获一些敏感信息,所以在v6.0以后,官方文档里面建议使用 Buffer.from()接口去创建Buffer对象。

当你需要一个Buffer对象,可以通过以下方式分配指定大小的Buffer对象。

// 创建一个长度为 100、且用 0 填充的 Buffer。
const buf = Buffer.alloc(5);

console.log(buf);
//  

Buffer.alloc(size[, fill[, encoding]]): 返回一个指定大小的Buffer实例,如果没有设置fill,则默认填满 0

  • size Buffer 的所需长度。
  • fill | | | 用于预填充新 Buffer 的值。默认值: 0
  • encoding 如果 fill 是一个字符串,则这是它的字符编码。默认值: 'utf8'

Node以8KB为界限来区分buffer是大对象还是小对象:

buffer.poolSize =  8 * 1024; 

这个8KB(8192)的值也就是每个slab的大小值,在JavaScript层面,以它作为单位单元进行内存的分配。

1 . 分配小Buffer对象。

如果指定Buffer的大小少于8KB,Node会按照小对象的方式进行分配。分配过程中主要使用一个局部变量pool作为中间处理对象,处于分配状态的slab单元都指向它。

var  pool;
function allocPool(){
   pool = Buffer.allocUnsafeSlow(buffer.poolSize);
   pool.used = 0 ;
}

构造小对象时将会去检查pool对象,如果pool没有被创建,将会见一个新的slab单元指向它:

if(!pool||pool.length-pool.used

同时当前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;

在pool初始化大小为8KB,未被分配时执行

const buf = Buffer.alloc(5, 0);
//

小结:
  1. 真正的内存是在NodeC++层面提供的,JavaScript层面只是使用它。
  2. 进行小而频繁的Buffer操作时,采用slab的机制进行预先申请和事后分配,使得JavaScript到操作系统之间不必有过多的内存申请方面 的系统调用。
  3. 大块的buffer而言,则直接使用c++层面 提供的内存,无需细腻的分配操作。

你可能感兴趣的:(2. Buffer内存分配)