Nodejs的Buffer对象(中)—— 基本原理与使用要点

原创 Lin_Grady 教程 nodejs 217阅读 2018-07-20 10:17:08 举报

前言

一开始全文是基于《Nodejs深入浅出》,但是因为有段年头有些API已经是被废弃的,而且越往下走问题越多,最后只好自从查阅资料从头开始研究,所以这里的demo都是以Nodejs10.0为准。
因为内容实在太多,已经超过20000字限制,而且太长看着太烦了,所以我会分成三篇博文发布,后面发布了会补上跳转地址,分别是:
Nodejs的Buffer对象(上)—— API废弃与更新 (基于官网文档)
Nodejs的Buffer对象(中)—— 基本原理与使用要点(基于nodejs 深入浅出)
Nodejs的Buffer对象(下)—— 源代码实现浅析(基于nodejs源码)

因为靠自己整理输出,如果有哪些遗漏错误希望可以指教一下。

Buffer 结构

模块结构

Buffer是一个典型的Javascript与C++结合的模块,性能部分用C++实现,非性能部分用Javascript实现。因为属於核心模块,所以Nodejs在进程启动的时候就已经加载好了,所以无需引入直接使用。
上次说过因为Buffer属於非V8分配的堆外内存,所以非常适用於大多数场景下的大内存操作。

Nodejs内存有关文章请看Nodejs内存控制

Buffer对象

Buffer对象类似数组,它的元素為16进制的两位数,即0~255的数值。

十六进制(英文名称:Hexadecimal),是计算机中数据的一种表示方法。同我们日常生活中的表示法不一样。它由0-9,A-F组成,字母不区分大小写。与10进制的对应关系是:0-9对应0-9;A-F对应10-15;N进制的数可以用0~(N-1)的数表示,超过9的用字母A-F。

之所以说Buffer对象和数组很像是因为它们实例化方式,访问length长度和下标访问赋值元素都一样。

注意:如果赋值非整数则捨弃小数,小於 0 会逐次加 256 ,大於 255 则逐次减 256。

Buffer内存分配

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

为了高效使用申请的内存,Nodejs采用了 slab 动态内存管理机制,简单来说就是一块申请好的固定大小的内存区域,有三种状态:

  • full:完全分配
  • parital:部分分配
  • empty:没有被分配

Nodejs以 8KB 作为界限区分大小对象,也是每个slab的大小值,在Javascript层面作为单位单元进行内存分配。

分配小对象

如果指定大小小于8KB,Nodejs会按照小对象的方式进行分配,主要使用一个局部变量 allocPool 作为中间处理对象,处于分配状态的slab单元都指向它。源码如下:


|--------------------------------------------| 8KB的pool

|
offset:0

当前slab处于empty状态,构造小Buffer对象的时候会去检查allocPool对象,如果allocPool没有被创建将会创建新的slab单元指向它。
同时当前Buffer对象的parent属性指向该slab,并记录下是从这个slab的哪个位置(offset)开始使用,slab对象也会记录自身使用了多少字节,如下。

|||||-------------------------------------------------| 8KB的pool
    |
    offset:88

这时候的slab状态为partial,当再次创建一个Buffer对象时,构造过程会判断这个slab剩余空间是否足够使用并更新分配状态,如果不够会构建新的slab,原有slab的剩余空间就被浪费了。

例如分别构建1个字节和8192字节

源码如下:

除非slab上的Buffer对象都被释放且可回收,否则即使只有一个字节实际上也可能会占据8KB内存。

分配大对象

SlowBuffer 类对象已被废弃的,官方使用 Buffer.allocUnsafeSlow() 代替。

小结

Javascript层面只是提供给使用Buffer对象,真正的内存还是Nodejs的C++层面提供。
分配小Buffer对象是采用slab的机制进行预先申请和事后分配。
分配大Buffer对象是直接由C++层面提供的独享内存。

Buffer 处理

支持编码

支持的字符编码描述
ascii仅支持 7 位 ASCII 数据。如果设置去掉高位的话,这种编码是非常快的
utf8多字节编码的 Unicode 字符。许多网页和其他文档格式都使用 UTF-8
utf16le2 或 4 个字节,小字节序编码的 Unicode 字符。支持代理对(U+10000 至 U+10FFFF)
ucs2'utf16le' 的别名
base64Base64 编码。当从字符串创建 Buffer 时,按照 RFC4648 第 5 章的规定,这种编码也将正确地接受“URL 与文件名安全字母表”
latin1一种把 Buffer 编码成一字节编码的字符串的方式(由 IANA 定义在 RFC1345 第 63 页,用作 Latin-1 补充块与 C0/C1 控制码)
binary'latin1' 的别名
hex将每个字节编码为两个十六进制字符

字符串转Buffer

字符串转Buffer主要通过构造函数完成,这种方式只能存储一种编码类型。

Buffer.from(string[, encoding])

一个Buffer对象可以存储不同编码类型的字符串转码值,需要调用到 write() 方法。

buf.write(string[, offset[, length]][, encoding])

参数描述
string写入 buf 的字符串
offset [integer]开始写入 string 前要跳过的字节数。默认: 0
length [integer]写入的字节数。默认: buf.length - offset
encoding [string]string 的字符编码。默认: 'utf8'

根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。 length 参数是写入的字节数。 如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入。

注意:每种编码所用字节长度可能不同,处理需谨慎。

Buffer转字符串

buf.toString([encoding], [start], [end])

Buffer编码兼容

Nodejs的Buffer对象支持的编码类型有限,只有少数几种支持在字符串和Buffer之间转换,Buffer提供了 isEncoding() 判断编码是否支持转换。

对于不支持的编码类型也有很多模块库可以实现,例如
iconv-js:通过C++调用libiconv库完成
iconv-lite:通过纯Javascript实现,更加轻量,无需转换性能更好

我们用iconv-lite为例,先安装依赖

yarn add iconv-lite

然后分别执行字符串和buffer的转换

(完整代码可以执行Buffer-demo的 lesson1 查看效果)
同时还提供一个函数判断该模块是否支持某种编码类型

目前支持编码类型相当全面,详情请看Supported Encodings

  • Node.js Native encodings: utf8, ucs2 / utf16le, ascii, binary, base64, hex
  • Unicode: UTF-16BE, UTF-16 (with BOM)
  • Single-byte:
  • Windows codepages: 874, 1250-1258 (aliases: cpXXX, winXXX, windowsXXX)
  • ISO codepages: ISO-8859-1 - ISO-8859-16
  • IBM codepages: 437, 737, 775, 808, 850, 852, 855-858, 860-866, 869, 922, 1046, 1124, 1125, 1129, 1133, 1161-1163 (aliases cpXXX, ibmXXX)
  • Mac codepages: maccroatian, maccyrillic, macgreek, maciceland, macroman, macromania, macthai, macturkish, macukraine, maccenteuro, macintosh
  • KOI8 codepages: koi8-r, koi8-u, koi8-ru, koi8-t
  • Miscellaneous: armscii8, rk1048, tcvn, georgianacademy, georgianps, pt154, viscii, iso646cn, iso646jp, hproman8, tis620
  • Multi-byte:
  • Japanese: Shift_JIS, Windows-31j, Windows932, EUC-JP
  • Chinese: GB2312, GBK, GB18030, Windows936, EUC-CN
  • Korean: KS_C_5601, Windows949, EUC-KR
  • Taiwan/Hong Kong: Big5, Big5-HKSCS, Windows950

Buffer拼接

实际开发的常用场景是需要使用流方式逐步读写文件,我们现在目录下弄个 test.txt 文本作为测试,随便输首中英文歌词做比较。

然后同目录下新建脚本 lesson2.js 读取内容看看。

(完整代码可以执行Buffer-demo的 lesson2 查看效果)
执行脚本之后输出文本很正常,但是里面有个潜藏的隐患

data += chunk;

这行代码会隐性执行toString()操作,正常的英文内容自然没问题,但是对于宽字节的中文来说就是定时炸弹了。
我们稍微修改一下配置让文件流每次只读取Buffer长度11个字节,当前目录新建脚本 lesson3.js执行。

(完整代码可以执行Buffer-demo的 lesson3 查看效果)
现在我们能看到数不清的乱码出现了。

乱码如何产生

我们都知道从 fs.createReadStream() 读取出来的是Buffer对象,由于我们限定了读取字节数因此会发生截断Buffer的情况,所以当每段截取Buffer在输出的时候那些无法形成文字的只能显示乱码,这就是为什么乱码的位置零零散散遍布全文。

readable.setEncoding(encoding)

既然知道乱码原因是源自Buffer对象的不完整解析,如果我们改变读取文件的格式会怎样呢?
我们稍微修改一下配置让文件流以utf8格式编译,当前目录新建脚本 lesson4.js执行。

(完整代码可以执行Buffer-demo的 lesson4 查看效果)
执行脚本之后再看发现文本内容即使被截断也能正常显示了。

setEncoding原理

即使设置编码类型之后也无法解释为什么可以正常输出问题,因为脚本还是以截断字节的方式读取拼接,因此最可能是每次读取流之后不再隐性执行toString()方法而是在读取完毕之后再一起执行。

带着合理猜想,我们稍微在读取阶段看看chunk是不是Buffer格式,当前目录新建脚本 lesson5.js执行。

(完整代码可以执行Buffer-demo的 lesson5 查看效果)
执行脚本之后很诧异结果和我想的并不一样,尽管不是完整顺序文本,但是的的确确能够正常输出中文。

事实上在调用setEncoding()的时候可读流对象在内部设置了一个 decoder 对象,每次读取data事件都通过该对象进行Buffer到字符串的解码才传递给调用者,至于怎么解决字节被截断的问题,这就需要接着往下看了。

StringDecoder 类

  • new StringDecoder([encoding])
    创建一个新的 StringDecoder 实例,把 Buffer 对象解码成字符串,但会保留编码过的多字节 UTF-8 与 UTF-16 字符
  • stringDecoder.end([buffer])
    以字符串的形式返回内部 buffer 中剩余的字节,残缺的 UTF-8 与 UTF-16 字符的字节会被替换成符合字符编码的字符,如果提供了 buffer 参数,则在返回剩余字节之前会再执行一次 stringDecoder.write()
  • stringDecoder.write(buffer)
    返回一个解码后的字符串,并确保返回的字符串不包含 Buffer 末尾残缺的多字节字符,残缺的多字节字符会被保存在一个内部的 buffer 中用于下次调用 stringDecoder.write()stringDecoder.end()

基本用法

(完整代码可以执行Buffer-demo的 lesson6 查看效果)

当一个 Buffer 实例被写入 StringDecoder 实例时,会使用一个内部的 buffer 来确保解码后的字符串不会包含残缺的多字节字符。 残缺的多字节字符会被保存在这个 buffer 中,直到下次调用 stringDecoder.write() 或直到 stringDecoder.end() 被调用。

例子,欧元符号()的三个 UTF-8 编码的字节被分成三次操作写入:

(完整代码可以执行Buffer-demo的 lesson7 查看效果)

正确拼装Buffer

因为StringDecoder 类只支持UTF-8、Base64和UCS-2/UTF-16LE,所以setEncoding()只能解决大部分问题,所以还是得上到之前说的转码库解决。
接着上面代码修改,先去掉setEncoding方法,用一个数组存储所有Buffer片段并记录总长度,接受完所有数据之后合并成一个Buffer对象再转码。

(完整代码可以执行Buffer-demo的 lesson8 查看效果)
依赖库也能实现想要的输出结果了。

性能

Buffer在文件I/O和网络I/O运用广泛,特别在网络传输中会转换成Buffer进行二进制数据传输。

网络请求

我们做一些性能测试看看效果,构造一个简单的字符串返回给客户端。新建脚本 lesson9.js 复制下面代码并启动。

(完整代码可以执行Buffer-demo的 lesson9 查看效果)
我们使用loadtest库来做一次压力测试,设置100秒内200并发量,新开一个终端执行以下命令

loadtest -c 200 -t 100 http://127.0.0.1:3000

(不知道是什么东西的话可以看我直接翻译文档的博文Loadtest库做负载测试。)

大概等个100秒让它慢慢压测输出,结果大概如下

[Tue Jul 17 2018 16:28:42 GMT+0800 (中国标准时间)] INFO Requests: 0, requests per second: 0, mean latency: 0 ms
[Tue Jul 17 2018 16:28:47 GMT+0800 (中国标准时间)] INFO Requests: 4829, requests per second: 979, mean latency: 203.1 ms
[Tue Jul 17 2018 16:28:52 GMT+0800 (中国标准时间)] INFO Requests: 9933, requests per second: 1021, mean latency: 196.2 ms
[Tue Jul 17 2018 16:28:57 GMT+0800 (中国标准时间)] INFO Requests: 15070, requests per second: 1028, mean latency: 195.3 ms
[Tue Jul 17 2018 16:29:02 GMT+0800 (中国标准时间)] INFO Requests: 20257, requests per second: 1038, mean latency: 193.2 ms
[Tue Jul 17 2018 16:29:07 GMT+0800 (中国标准时间)] INFO Requests: 25397, requests per second: 1029, mean latency: 193.1 ms
[Tue Jul 17 2018 16:29:12 GMT+0800 (中国标准时间)] INFO Requests: 30598, requests per second: 1041, mean latency: 192.3 ms
[Tue Jul 17 2018 16:29:17 GMT+0800 (中国标准时间)] INFO Requests: 35838, requests per second: 1049, mean latency: 191.3 ms
[Tue Jul 17 2018 16:29:22 GMT+0800 (中国标准时间)] INFO Requests: 40987, requests per second: 1032, mean latency: 194.4 ms
[Tue Jul 17 2018 16:29:27 GMT+0800 (中国标准时间)] INFO Requests: 46127, requests per second: 1029, mean latency: 193.9 ms
[Tue Jul 17 2018 16:29:32 GMT+0800 (中国标准时间)] INFO Requests: 51312, requests per second: 1038, mean latency: 193.6 ms
[Tue Jul 17 2018 16:29:37 GMT+0800 (中国标准时间)] INFO Requests: 56521, requests per second: 1044, mean latency: 192.2 ms
[Tue Jul 17 2018 16:29:42 GMT+0800 (中国标准时间)] INFO Requests: 61700, requests per second: 1036, mean latency: 192.7 ms
[Tue Jul 17 2018 16:29:47 GMT+0800 (中国标准时间)] INFO Requests: 66925, requests per second: 1046, mean latency: 191.6 ms
[Tue Jul 17 2018 16:29:52 GMT+0800 (中国标准时间)] INFO Requests: 72080, requests per second: 1032, mean latency: 193.2 ms
[Tue Jul 17 2018 16:29:57 GMT+0800 (中国标准时间)] INFO Requests: 77284, requests per second: 1042, mean latency: 192.8 ms
[Tue Jul 17 2018 16:30:02 GMT+0800 (中国标准时间)] INFO Requests: 82519, requests per second: 1048, mean latency: 191.1 ms
[Tue Jul 17 2018 16:30:07 GMT+0800 (中国标准时间)] INFO Requests: 87754, requests per second: 1048, mean latency: 191 ms
[Tue Jul 17 2018 16:30:12 GMT+0800 (中国标准时间)] INFO Requests: 93009, requests per second: 1052, mean latency: 190 ms
[Tue Jul 17 2018 16:30:17 GMT+0800 (中国标准时间)] INFO Requests: 98237, requests per second: 1046, mean latency: 191.5 ms
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Target URL: http://127.0.0.1:3000
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Max time (s): 100
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Concurrency level: 200
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Agent: none
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Completed requests: 103447
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Total errors: 0
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Total time: 100.000453852 s
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Requests per second: 1034
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Mean latency: 193.1 ms
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO Percentage of the requests served within a certain time
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO 50% 192 ms
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO 90% 198 ms
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO 95% 201 ms
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO 99% 209 ms
[Tue Jul 17 2018 16:30:22 GMT+0800 (中国标准时间)] INFO 100% 313 ms (longest request)

然后我们新建 lesson10.js 脚本在返回之前先把字符串转成Buffer对象,重复上面操作。

(完整代码可以执行Buffer-demo的 lesson10 查看效果)
压测结果如下

[Tue Jul 17 2018 16:23:45 GMT+0800 (中国标准时间)] INFO Requests: 0, requests per second: 0, mean latency: 0 ms
[Tue Jul 17 2018 16:23:49 GMT+0800 (中国标准时间)] INFO Requests: 4718, requests per second: 959, mean latency: 208.2 ms
[Tue Jul 17 2018 16:23:54 GMT+0800 (中国标准时间)] INFO Requests: 9782, requests per second: 1013, mean latency: 197.7 ms
[Tue Jul 17 2018 16:23:59 GMT+0800 (中国标准时间)] INFO Requests: 14893, requests per second: 1023, mean latency: 195.7 ms
[Tue Jul 17 2018 16:24:04 GMT+0800 (中国标准时间)] INFO Requests: 20027, requests per second: 1027, mean latency: 194.4 ms
[Tue Jul 17 2018 16:24:09 GMT+0800 (中国标准时间)] INFO Requests: 24891, requests per second: 969, mean latency: 204.9 ms
[Tue Jul 17 2018 16:24:14 GMT+0800 (中国标准时间)] INFO Requests: 29683, requests per second: 965, mean latency: 209.8 ms
[Tue Jul 17 2018 16:24:19 GMT+0800 (中国标准时间)] INFO Requests: 34509, requests per second: 966, mean latency: 207.4 ms
[Tue Jul 17 2018 16:24:24 GMT+0800 (中国标准时间)] INFO Requests: 39384, requests per second: 977, mean latency: 204.6 ms
[Tue Jul 17 2018 16:24:29 GMT+0800 (中国标准时间)] INFO Requests: 44280, requests per second: 980, mean latency: 203.9 ms
[Tue Jul 17 2018 16:24:34 GMT+0800 (中国标准时间)] INFO Requests: 49238, requests per second: 992, mean latency: 202.2 ms
[Tue Jul 17 2018 16:24:39 GMT+0800 (中国标准时间)] INFO Requests: 53942, requests per second: 938, mean latency: 211.6 ms
[Tue Jul 17 2018 16:24:44 GMT+0800 (中国标准时间)] INFO Requests: 58745, requests per second: 973, mean latency: 209.3 ms
[Tue Jul 17 2018 16:24:49 GMT+0800 (中国标准时间)] INFO Requests: 63749, requests per second: 1002, mean latency: 199.9 ms
[Tue Jul 17 2018 16:24:54 GMT+0800 (中国标准时间)] INFO Requests: 68477, requests per second: 946, mean latency: 210.7 ms
[Tue Jul 17 2018 16:24:59 GMT+0800 (中国标准时间)] INFO Requests: 73423, requests per second: 989, mean latency: 203.1 ms
[Tue Jul 17 2018 16:25:04 GMT+0800 (中国标准时间)] INFO Requests: 78507, requests per second: 1017, mean latency: 195.6 ms
[Tue Jul 17 2018 16:25:09 GMT+0800 (中国标准时间)] INFO Requests: 83676, requests per second: 1034, mean latency: 194.8 ms
[Tue Jul 17 2018 16:25:14 GMT+0800 (中国标准时间)] INFO Requests: 88785, requests per second: 1022, mean latency: 195.2 ms
[Tue Jul 17 2018 16:25:19 GMT+0800 (中国标准时间)] INFO Requests: 93830, requests per second: 1009, mean latency: 197.1 ms
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO Target URL: http://127.0.0.1:3000
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO Max time (s): 100
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO Concurrency level: 200
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO Agent: none
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO Completed requests: 98914
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO Total errors: 0
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO Total time: 100.000624897 s
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO Requests per second: 989
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO Mean latency: 201.9 ms
[Tue Jul 17 2018 16:25:24 GMT+0800 (中国标准时间)] INFO
[Tue Jul 17 2018 16:25:25 GMT+0800 (中国标准时间)] INFO Percentage of the requests served within a certain time
[Tue Jul 17 2018 16:25:25 GMT+0800 (中国标准时间)] INFO 50% 199 ms
[Tue Jul 17 2018 16:25:25 GMT+0800 (中国标准时间)] INFO 90% 215 ms
[Tue Jul 17 2018 16:25:25 GMT+0800 (中国标准时间)] INFO 95% 221 ms
[Tue Jul 17 2018 16:25:25 GMT+0800 (中国标准时间)] INFO 99% 248 ms
[Tue Jul 17 2018 16:25:25 GMT+0800 (中国标准时间)] INFO 100% 374 ms (longest request)

两次结果对比如下:

输出字符串Buffer对象
Completed requests9841689464
Total time100.000453852 s100.000624897 s
Requests per second1034989
Mean latency193.1 ms201.9 ms
50%192 ms199 ms
90%192 ms215 ms
95%201 ms221 ms
99%209 ms248 ms
100%313 ms374 ms

可以看到性能相差无几字符串还略胜一筹。

这就尴尬了,根据《Nodejs深入浅出》的内容应该分别是:
测试的QPS(每秒查询次数)是2527.64,传输率为每秒25370.16 KB。
测试的QPS(每秒查询次数)是4843.28,传输率为每秒48 612.56 KB。
暂时也不知道怎么找出原因,然后下面的说法现在也不能尽信了。

将页面的动态内容和静态内容分离,通过预先转换静态内容为Buffer对象可以有效减少CPU的重复使用,节省服务器资源。由于文件本身是二进制数据,在不需要改变情况下尽量读取Buffer直接传输,不做额外转换。

文件读取

上面提到过的文件读取中有个 highWaterMark设置 对性能也至关重要,例如 fs.createReadStream() 的工作方式是在内存中准备一段Buffer,然后在 fs.read() 读取时逐步从磁盘中将字节复制到Buffer中。完成一次读取时则从这个Buffer中通过 slice() 方法取出部分数据作为一个小Buffer对象,再通过data事件传递给调用方。如果Buffer用完则重新分配一个,否则继续使用。

在理想状况下每次读取的长度就是用户指定的 highWaterMark设置 长度。但是可能读到文件结尾或者文件本身没有设置长度这么大,这个预先指定的Buffer对象将有部分剩余可以分配给下次使用。 pool 是常驻内存,只有当pool单元剩余数量小于128(kMinPoolSpace)字节时才会重新分配一个新的Buffer对象。Nodejs源码如下:

(这里应该不是Buffer而是createReadStream的源代码)

这里与Buffer的内存分配比较类似,highWaterMark的大小影响如下:

  • 对Buffer内存的分配和使用有一定影响
  • 设置过小可能导致系统调用次数过多

读取一个相同的大文件时候,highWaterMark的大小与读取速度成正比。

完整demo

memory-demo
里面demo都是自己写的,没有依赖可以直接跑。懒得写就去上面搬走看,懒得搬就直接看文章,大部分代码连输出信息都给你们了。

参考资料

Node.js v10.6.0 文档
nodejs 深入浅出
node/lib/buffer.js

评论 ( 0 )
最新评论
暂无评论

赶紧努力消灭 0 回复