이 기사에서는 Node.js의 버퍼를 이해하고 버퍼 구조, 버퍼 메모리 할당, 버퍼 스플라이싱 등에 대해 살펴보겠습니다. 도움이 되길 바랍니다!
JavaScript
는 문자열 연산에 매우 친숙합니다.JavaScript
对于字符串的操作十分友好
Buffer
是一个像Array
的对象,主要用于操作字节。
Buffer
是一个典型的JavaScript和C++结合的模块,将性能相关部分用C++实现,将非性能相关部分用JavaScript实现。
Buffer所占用的内存不是通过V8分配,属于堆外内存。 由于V8垃圾回收性能影响,将常用的操作对象用更高效和专有的内存分配回收政策来管理是个不错的思路。
Buffer在Node进程启动时就已经价值,并且放在全局对象(global)上。所以使用buffer无需require引入
Buffer对象的元素未16进制的两位数,即0-255的数值
let buf01 = Buffer.alloc(8); console.log(buf01); // <Buffer 00 00 00 00 00 00 00 00>
可以使用fill
填充buf的值(默认为utf-8
编码),如果填充的值超过buffer,将不会被写入。
如果buffer长度大于内容,则会反复填充
如果想要清空之前填充的内容,可以直接fill()
buf01.fill('12345678910') console.log(buf01); // <Buffer 31 32 33 34 35 36 37 38> console.log(buf01.toString()); // 12345678
如果填入的内容是中文,在utf-8
的影响下,中文字会占用3个元素,字母和半角标点符号占用1个元素。
let buf02 = Buffer.alloc(18, '开始我们的新路程', 'utf-8'); console.log(buf02.toString()); // 开始我们的新
Buffer
受Array类型
影响很大,可以访问length属性得到长度,也可以通过下标访问元素,也可以通过indexOf查看元素位置。
console.log(buf02); // <Buffer e5 bc 80 e5 a7 8b e6 88 91 e4 bb ac e7 9a 84 e6 96 b0> console.log(buf02.length) // 18字节 console.log(buf02[6]) // 230: e6 转换后就是 230 console.log(buf02.indexOf('我')) // 6:在第7个字节位置 console.log(buf02.slice(6, 9).toString()) // 我: 取得<Buffer e6 88 91>,转换后就是'我'
如果给字节赋值不是0255之间的整数,或者赋值时小数时,赋值小于0,将该值逐次加256.直到得到0255之间的整数。如果大于255,就逐次减去255。 如果是小数,舍去小数部分(不做四舍五入)
Buffer
对象的内存分配不是在V8的堆内存中,而是在Node的C++层面实现内存的申请。 因为处理大量的字节数据不能采用需要一点内存就向操作系统申请一点内存的方式。为此Node在内存上使用的是在C++层面申请内存,在JavaScript
中分配内存的方式
Node
采用了slab分配机制
,slab
是以中动态内存管理机制,目前在一些*nix
操作系统用中有广泛的应用,比如Linux
slab
就是一块申请好的固定大小的内存区域,slab具有以下三种状态:
Node以8KB为界限来区分Buffer是大对象还是小对象
console.log(Buffer.poolSize); // 8192
这个8KB的值就额是每个slab的大小值,在JavaScript层面,以它作为单位单元进行内存的分配
如果指定Buffer
大小小于8KB,Node会按照小对象方式进行分配
buffer
对象1024KB,当前的slab
会被占用1024KB,并且记录下是从这个slab
的哪个位置开始使用的buffer
对象,大小为3072KB。 构造过程会判断当前slab
剩余空间是否足够,如果足够,使用剩余空间,并更新slab
的分配状态。 3072KB空间被使用后,目前此slab剩余空间4096KB。buffer
,当前slab空间不足,会构造新的slab
Buffer
는 Array
와 같은 객체이며 주로 사용됩니다. 바이트를 조작합니다. 버퍼 구조
버퍼
는 JavaScript와 C++를 결합한 대표적인 모듈입니다. 성능 관련 부분은 C++로 구현됩니다. 성능과 관련되지 않은 부분은 JavaScript로 구현됩니다.
🎜🎜버퍼 점유된 메모리는 V8을 통해 할당되지 않으며 오프힙 메모리에 속합니다. V8 가비지 수집이 성능에 미치는 영향으로 인해 일반적으로 사용되는 작업 개체를 보다 효율적이고 독점적인 메모리 할당 및 재활용 정책으로 관리하는 것이 좋습니다. 🎜🎜Buffer는 Node 프로세스가 시작될 때 이미 값이 지정되어 전역 개체(global)에 배치됩니다. 따라서 버퍼를 사용할 때 🎜
Buffer.alloc(1) Buffer.alloc(8192)🎜은
fill
을 사용하여 buf 값을 채울 수 있습니다(기본값은 utf-8
인코딩입니다). 채워진 값이 버퍼를 초과하면 기록되지 않습니다. 🎜🎜버퍼 길이가 내용보다 크면 반복적으로 채워집니다🎜🎜이전에 채워진 내용을 지우고 싶다면 직접
fill()
을 하면 됩니다🎜 const fs = require('fs'); let rs = fs.createReadStream('./静夜思.txt', { flags:'r'}); let str = '' rs.on('data', (chunk)=>{ str += chunk; }) rs.on('end', ()=>{ console.log(str); })🎜채워진 내용이 중국어라면
utf-8
의 영향으로 한자는 3요소, 글자와 반각 구두점은 1요소를 차지하게 됩니다. 🎜fs.createReadStream('./静夜思.txt', { flags:'r', highWaterMark: 11});🎜
버퍼
는 배열 유형
의 영향을 크게 받습니다. 길이 속성에 액세스하여 길이를 얻을 수도 있고, 아래 첨자를 통해 요소에 액세스할 수도 있습니다. 또한 indexOf를 통해 요소 위치를 봅니다. 🎜fs.createReadStream('./静夜思.txt', { flags:'r', highWaterMark: 11, encoding:'utf-8'});🎜바이트에 할당된 값이 0255 사이의 정수가 아니거나 할당된 값이 소수인 경우 할당된 값이 0보다 작은 경우 다음을 추가하세요. 256을 0에서 255 사이의 정수를 얻을 때까지 하나씩 값으로 변환합니다. 255보다 크면 255를 하나씩 뺍니다. 소수이면 소수 부분을 버립니다(반올림 없음)🎜
버퍼
의 메모리 할당 V8의 힙 메모리에서는 메모리 적용이 Node.js의 C++ 레벨에서 구현됩니다. 왜냐하면 대용량 바이트 데이터를 처리할 때 메모리가 필요할 때 운영체제에서 메모리를 일부 신청할 수 없기 때문이다. 이러한 이유로 Node는 C++ 수준에서 메모리 애플리케이션을 사용하고 JavaScript
🎜🎜에서 메모리를 할당합니다. Node
는 슬랩 할당 메커니즘
, 를 채택합니다. slab
은 동적 메모리 관리 메커니즘으로, 현재 Linux
🎜🎜와 같은 일부 *nix
운영 체제에서 널리 사용됩니다. slab
은 적용된 고정 크기 메모리 영역 🎜const { StringDecoder } = require('string_decoder'); let s1 = Buffer.from([0xe7, 0xaa, 0x97, 0xe5, 0x89, 0x8d, 0xe6, 0x98, 0x8e, 0xe6, 0x9c]) let s2 = Buffer.from([0x88, 0xe5, 0x85, 0x89, 0xef, 0xbc, 0x8c, 0x0d, 0x0a, 0xe7, 0x96]) console.log(s1.toString()); console.log(s2.toString()); console.log('------------------'); const decoder = new StringDecoder('utf8'); console.log(decoder.write(s1)); console.log(decoder.write(s2));
🎜8KB 값은 JavaScript 수준에서 각 슬랩의 크기를 사용합니다. 메모리 할당 단위로🎜
Buffer
를 지정하는 경우 크기가 8KB 미만이면 Node에서 Small Object 방식에 따라 할당합니다🎜slab
는 1024KB를 차지하며 이 slab
가 사용되는 위치를 기록합니다🎜🎜🎜🎜buffer
객체를 생성합니다. 시공 과정에서 현재 슬래브
의 남은 공간이 충분한지 판단합니다. 충분하다면 남은 공간을 활용하고 슬래브
의 할당 상태를 업데이트합니다. 3072KB 공간을 사용한 후 이 슬래브의 남은 공간은 현재 4096KB입니다. 🎜🎜🎜🎜버퍼
가 생성되면 현재 슬래브 공간이 부족하여 새로운 슬래브
가 구축됩니다(이 남은 공간이 낭비됩니다) 🎜🎜🎜🎜🎜🎜예를 들어 다음 예에서는 🎜Buffer.alloc(1) Buffer.alloc(8192)
第一个slab
中只会存在1字节的buffer对象,而后一个buffer对象会构建一个新的slab存放
由于一个slab可能分配给多个Buffer对象使用,只有这些小buffer对象在作用域释放并都可以回收时,slab的空间才会被回收。 尽管只创建1字节的buffer对象,但是如果不释放,实际是8KB的内存都没有释放
小结:
真正的内存是在Node的C++层面提供,JavaScript层面只是使用。当进行小而频繁的Buffer操作时,采用slab的机制进行预先申请和时候分配,使得JavaScript到操作系统之间不必有过多的内存申请方面的系统调用。 对于大块的buffer,直接使用C++层面提供的内存即可,无需细腻的分配操作。
buffer在使用场景中,通常是以一段段的方式进行传输。
const fs = require('fs'); let rs = fs.createReadStream('./静夜思.txt', { flags:'r'}); let str = '' rs.on('data', (chunk)=>{ str += chunk; }) rs.on('end', ()=>{ console.log(str); })
以上是读取流的范例,data时间中获取到的chunk对象就是buffer对象。
但是当输入流中有宽字节编码(一个字占多个字节
)时,问题就会暴露。在str += chunk
中隐藏了toString()
操作。等价于str = str.toString() + chunk.toString()
。
下面将可读流的每次读取buffer长度限制为11.
fs.createReadStream('./静夜思.txt', { flags:'r', highWaterMark: 11});
输出得到:
上面出现了乱码,上面限制了buffer长度为11,对于任意长度的buffer而言,宽字节字符串都有可能存在被截断的情况,只不过buffer越长出现概率越低。
但是如果设置了encoding
为utf-8
,就不会出现此问题了。
fs.createReadStream('./静夜思.txt', { flags:'r', highWaterMark: 11, encoding:'utf-8'});
原因: 虽然无论怎么设置编码,流的触发次数都是一样,但是在调用setEncoding
时,可读流对象在内部设置了一个decoder对象
。每次data事件都会通过decoder对象
进行buffer到字符串的解码,然后传递给调用者。
string_decoder
模块提供了用于将 Buffer 对象解码为字符串(以保留编码的多字节 UTF-8 和 UTF-16 字符的方式)的 API
const { StringDecoder } = require('string_decoder'); let s1 = Buffer.from([0xe7, 0xaa, 0x97, 0xe5, 0x89, 0x8d, 0xe6, 0x98, 0x8e, 0xe6, 0x9c]) let s2 = Buffer.from([0x88, 0xe5, 0x85, 0x89, 0xef, 0xbc, 0x8c, 0x0d, 0x0a, 0xe7, 0x96]) console.log(s1.toString()); console.log(s2.toString()); console.log('------------------'); const decoder = new StringDecoder('utf8'); console.log(decoder.write(s1)); console.log(decoder.write(s2));
StringDecoder
在得到编码之后,知道了宽字节字符串在utf-8
编码下是以3个字节的方式存储的,所以第一次decoder.write
只会输出前9个字节转码的字符,后两个字节会被保留在StringDecoder
内部。
buffer在文件I/O和网络I/O中运用广泛,尤其在网络传输中,性能举足轻重。在应用中,通常会操作字符串,但是一旦在网络中传输,都需要转换成buffer,以进行二进制数据传输。 在web应用中,字符串转换到buffer是时时刻刻发生的,提高字符串到buffer的转换效率,可以很大程度地提高网络吞吐率。
如果通过纯字符串的方式向客户端发送,性能会比发送buffer对象更差,因为buffer对象无须在每次响应时进行转换。通过预先转换静态内容为buffer对象,可以有效地减少CPU重复使用,节省服务器资源。
可以选择将页面中动态和静态内容分离,静态内容部分预先转换为buffer的方式,使得性能得到提升。
在文件的读取时,highWaterMark
设置对性能影响至关重要。在理想状态下,每次读取的长度就是用户指定的highWaterMark
。
highWaterMark
大小对性能有两个影响的点:
更多node相关知识,请访问:nodejs 教程!!
위 내용은 Node.js의 Buffer 모듈을 간략하게 이해하세요.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!