描述
JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。但在处理像TCP流或文件流时,必须使用到二进制数据。
因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区
—— 菜鸟教程
创建buffer实例
在v6.0之前创建Buffer对象直接使用new Buffer()构造函数来创建对象实例,但是Buffer对内存的权限操作相比很大,可以直接捕获一些敏感信息,所以在v6.0以后,官方文档里面建议使用 Buffer.from() 接口去创建Buffer对象。
Buffer.from
- Buffer.from(array)
返回一个被 array 的值初始化的新的 Buffer 实例(传入的 array 的元素只能是数字,不然就会自动被 0 覆盖) - Buffer.from(arrayBuffer[, byteOffset[, length]])
返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。 - Buffer.from(buffer)
复制传入的 Buffer 实例的数据,并返回一个新的 Buffer 实例 - Buffer.from(string[, encoding])
返回一个被 string 的值初始化的新的 Buffer 实例,encoding默认utf8
其中31 32,是字符'1','2'的utf8编码16进制值
Buffer.alloc
Buffer.alloc(size[, fill[, encoding]])
返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0
Buffer.allocUnsafe
Buffer.allocUnsafe(size)
返回一个指定大小的 Buffer 实例,但是它不会被初始化,所以它可能包含敏感的数据
多次运行,会得到不同的结果。
其他API
类方法
- Buffer.isEncoding(encoding)
Buffer是否支持此编码
console.log(Buffer.isEncoding('utf8'));
console.log(Buffer.isEncoding('utf16'));
- Buffer.isBuffer(Object)
判断对象是否是buffer实例
console.log(Buffer.isBuffer('test'));
console.log(Buffer.isBuffer(Buffer.alloc(10)));
- Buffer.byteLength(string, [encoding])
字符串所占字节长度,默认utf8
console.log(Buffer.byteLength('hello', 'utf8'));
const buffer = Buffer.from('hello'); //aGVsbG8=
console.log(Buffer.byteLength(buffer.toString('base64'), 'base64'));
- Buffer.concat(Buffer[], [length])
将多个buffer对象拼接成一个新的buffer对象。length是指合并后的buffer对象长度,如果不指定,nodejs内部会先计算
const buffer1 = Buffer.from('hello');
const buffer2 = Buffer.from(' nodejs');
const buffer = Buffer.concat([buffer1, buffer2]);
console.log(`${buffer.toString()}, ${buffer1.toString()}, ${buffer2.toString()}`);
实例属性
- length
返回buffer对象所占内存长度,单位byte
const buffer = Buffer.from('富途');
console.log(buffer.length);
实例方法
- toString([encoding], [start], [end])
buffer转字符串,这里之前用过了,不再举例。可支持的encoding见后文 - toJSON
const buffer = Buffer.from('hello');
console.log(buffer);
console.log(buffer.toJSON());
// JSON.stringify的对象是buffer时,会先调用toJSON方法
console.log(JSON.stringify(buffer));
- wite(string, [start], [encoding])
const buffer = Buffer.alloc(10);
buffer.write(' css', 1);
console.log('length is ' + buffer.length +', string is ' + buffer.toString());
- slice(start, end)
const buffer = Buffer.from('你好');
console.log(buffer.length);
const buffer2 = buffer.slice(0, 3);
console.log(buffer2.toString());
Buffer与字符编码
当Buffer与字符串之间转换时,可指定字符编码,默认utf8。
Node.js目前支持的字符编码如下:
- utf8 多字节的Unicode字符
- utf16le 多字节Unicode字符,与utf8不同的是,字符串中的每个字符都使用2或4个字节进行编码。Node.js中仅支持小端序的变体
- latin1 代表ISO-8859-1,仅支持U+0000~U+00FF的Unicode字符。每个字符都使用单个字节进行编码,不符合规范的字符将被截断并映射到该范围内。(之前的名称是binary)
Node.js还支持二进制转文本的编码:
- hex 每个字节编码为两个16进制字符
- base64 base64编码中的空白字符(空格、换行、制表)会被忽略
- base64url v14.18.0,v15.7.0 新增
hex:字符 -> buffer时,如果字符串不完全由偶数个十六进制的字符组成,会发生数据截断
内存分配
Nodejs内存
在了解buffer的内存分配前,我们先了解一下nodejs的内存
let mem = process.memoryUsage();
console.log(mem);
// 结果是,单位都是字节
{
rss: 193200128,
heapTotal: 173047808,
heapUsed: 170937264,
external: 942169,
arrayBuffers: 9386
}
- rss 驻留集
即进程在主内存设备(进程的内存还可能存在交换区或文件系统中)占用的空间量,包括所有C++和JavaScript的对象和代码 - heapTotal
V8堆内存,堆中总共申请的内存量 - heapUsed
V8堆内存,总重已使用的内存量 - external
绑定到v8管理的js对象的C++对象的内存使用量 - arrayBuffers
为ArrayBuffer和SharedArrayBuffer分配的内存,包括所有的nodejs buffer。这也包含在external值中。当nodejs作为嵌入库时,此值可能是0,这种情况下可能不会跟踪ArrayBuffer的分配
// 准备一个打印进程内存使用情况的函数
function showMemory() {
const format = (bytes) => {
return (bytes / 1024 / 1024).toFixed(2) + ' MB';
};
let mem = process.memoryUsage();
let str = 'Process memory\n ';
Object.keys(mem).forEach(key => {
str += key + ':' + format(mem[key])+ ' ';
});
console.log(str);
}
showMemory();
Buffer内存
1、node.js中的内存有两种,一是堆内存(由V8申请的),一是堆外内存(C++申请的)。buffer对象的内存是堆外内存。
function useMemory() {
let size = 9 * 1024 * 1024; // 9MB
let buffer = Buffer.alloc(size, 0);
return buffer;
}
let total = [];
for(let i = 0; i < 5; i++) {
console.log('i = ' + i);
showMemory();
total.push(useMemory());
}
可以看到,rss、heapTotal、heapUsed变化极小,而external、arrayBuffers都是9MB递增。
2、Nodejs以8kb做为分界线来区分buffer是小对象还是大对象。
SlowBuffer是C++中定义的类,C++中分配Buffer对象内存的实际指向对象,可通过buffer.parent访问到。
1)小Buffer对象
多个buffer对象共用一个slab,所有小buffer对象都回收时,slab 8kb的空间才会被回收。
(slab单元指向SlowBuffer)
这两个Buffer对象的parent是同一个
buffer释放前后相差8kb(左右)
2)大Buffer对象
直接分配一个SlowBuffer对象作为slab单元,这个slab单元会被这个大buffer对象独占。
参考文章
- 缓冲区与字符编码
- Nodejs memoryusage
- Buffer对象
- <深入浅出NodeJs> 朴灵