六、Buffer
1. Buffer结构
1.1 模块结构
- buffer的性能相关部分用C++实现,非性能部分用JavaScript实现;
- Node在进程启动时就已经加载了,并将其放在全局对象。
1.2 Buffer对象
- Buffer类似与数组,他的元素未16进制两位数。
- 不同编码所占的元素个数不相同,UTF8编码下一个中文字符占三个元素,字母和半角标点占一个字符。
- 可以通过
buffer.length
来获取Buffer对象的长度。 - 如果给Buffer元素赋值0-255以外的值,Buffer就会对其连续加上256,直到得到区间内的值为止;
- 若赋值小数,则会舍弃小数部分。
1.3 Buffer内存分配
-
Buffer对象的内存分配不是在V8的堆内存,而是Node在C++层面上实现内存的申请;
-
Node在内存上的使用应用的策略是:在C++层面申请内存,在Javascript层面分配内存;
-
Node采用了slab分配机制,用于高效地分配内存;
-
slab就是一块已经申请好的固定大小的内存区域,具有三种状态:
full:完全分配状态;
partial:部分分配状态;
empty:没有分配状态。
-
当我们需要一个Buffer对象,可以通过
new Buffer(size)
的方式来分配指定大小的对象; -
而Node以8KB来界定Buffer对象的大小:
Buffer.poolSize = 8 * 1024
这个8KB就是slab的值,在JavaScript层面,以它为单位进行内存分配。
1.4 分配小Buffer对象
-
若指定的Buffer对象大小小于8KB,则Node会按照小对象的方式进行分配:
-
Buffer在分配过程中主要使用一个局部变量pool作为中间处理对象,处于分配状态的slab都指向它:
var pool; function allocPool() { pool = new SlowBuffer(Buffer.poolSize); pool.used = 0; }
此时,slab处于empty状态。
-
构造小Buffer对象时,Node会去检查有没有Pool对象,如果没有被创建,将会创建一个新的slab并指向它:
if(!pool || pool.length - pool.used < this.length) { allocPool(); }
-
当前的Buffer对象的parent属性也指向这个slab,并记录下是从这个slab的哪个位置开始使用的,slab自身的used也需要记录使用了多少字节:
this.parent = pool; this.offset = pool.used; pool.used += this.length; // 如果used超过了8KB if(pool.used & 7) { pool.used = (pool.used + 8) & ~7; }
-
当再次创建一个Buffer对象的时候,构造过程中会判断这个slab的剩余空间是否足够。
如果够,直接使用剩余的空间并分配slab的分配状态;
如果slab的空间不够了,将会构造新
-