这时候的slab状态为partial。当再次创建一个Buffer对象时,构造过程中将会判断这个slab的剩余空间是否足够。如果足够,使用剩余空间,并更新slab的分配状态。下面的代码创建了一个新的Buffer对象,它会引起一次slab分配:
new Buffer(3000);//旧 Buffer.alloc(3000);//新
如果slab剩余的空间不够,将会构造新的slab,原slab中剩余的空间会造成浪费。例如,第一次构造1字节的Buffer对象,第二次构造8192字节的Buffer对象,由于第二次分配时slab中的空间不够,所以创建并使用新的slab,第一个slab的8KB将会被第一个1字节的Buffer对象独占。下面的代码一共使用了两个slab单元:
new Buffer(1);//旧 Buffer.alloc(1);//新 new Buffer(8192);//旧 Buffer.alloc(8192);//新
要注意的是,由于同一个slab可能分配给多个Buffer对象使用,只有这些小Buffer对象在作用域释放并都可以回收时,slab的8KB空间才会被回收。尽管创建了1个字节的Buffer对象,但是如果不释放它,实际可能是8KB的内存没有释放
2、分配大Buffer对象
如果需要超过8KB的Buffer对象,将会直接分配一个SlowBuffer对象作为slab单元,这个slab单元将会被这个大Buffer对象独占
// Big buffer, just alloc one this.parent = new SlowBuffer(this.length); this.offset = 0;
这里的SlowBuffer类是在C++中定义的,虽然引用buffer模块可以访问到它,但是不推荐直接操作它,而是用Buffer替代
上面提到的Buffer对象都是JavaScript层面的,能够被V8的垃圾回收标记回收。但是其内部的parent属性指向的SlowBuffer对象却来自于Node自身C++中的定义,是C++层面上的Buffer对象,所用内存不在V8的堆中
综上,真正的内存是在Node的C++层面提供的,JavaScript层面只是使用它。当进行小而频繁的Buffer操作时,采用slab的机制进行预先申请和事后分配,使得JavaScript到操作系统之间不必有过多的内存申请方面的系统调用。对于大块的Buffer而言,则直接使用C++层面提供的内存,而无需细腻的分配操作
转换
Buffer对象可以与字符串之间相互转换。目前支持的字符串编码类型有如下几种:ASCII、UTF-8、UTF-16LE/UCS-2、Base64、Binary、Hex
【write()】
一个Buffer对象可以存储不同编码类型的字符串转码的值,调用write()方法可以实现该目的
buf.write(string, [offset], [length], [encoding])
string <String> 要写入 buf 的字符串
offset <Integer> 开始写入 string 的位置。默认: 0
length <Integer> 要写入的字节数。默认: buf.length - offset
encoding <String> string 的字符编码。默认: 'utf8';返回: <Integer> 写入的字节数
根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。 length 参数是写入的字节数。 如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入
var buf = Buffer.alloc(5); console.log(buf); //<Buffer 00 00 00 00 00> var len = buf.write('test',1,3); console.log(buf);//<Buffer 00 74 65 73 00> console.log(len);/3
由于可以不断写入内容到Buffer对象中,并且每次写入可以指定编码,所以Buffer对象中可以存在多种编码转化后的内容。需要小心的是,每种编码所用的字节长度不同,将Buffer反转回字符串时需要谨慎处理
【toString()】
实现Buffer向字符串的转换也十分简单,Buffer对象的toString()可以将Buffer对象转换为字符串
buf.toString([encoding], [start], [end])
encoding - 使用的编码。默认为 'utf8'
start - 指定开始读取的索引位置,默认为 0
end - 结束位置,默认为缓冲区的末尾
返回 - 解码缓冲区数据并使用指定的编码返回字符串