Heim  >  Artikel  >  Web-Frontend  >  Verstehen Sie kurz das Buffer-Modul in Node.js

Verstehen Sie kurz das Buffer-Modul in Node.js

青灯夜游
青灯夜游nach vorne
2021-11-24 19:20:031530Durchsuche

Dieser Artikel führt Sie zum Verständnis des Puffers in Node.js. Werfen Sie einen Blick auf die Pufferstruktur, die Pufferspeicherzuweisung, das Pufferspleißen usw. Ich hoffe, er wird Ihnen hilfreich sein!

Verstehen Sie kurz das Buffer-Modul in Node.js

Buffer verstehen

JavaScript ist sehr benutzerfreundlich für String-OperationenJavaScript对于字符串的操作十分友好

Buffer是一个像Array的对象,主要用于操作字节。

Buffer结构

Buffer是一个典型的JavaScript和C++结合的模块,将性能相关部分用C++实现,将非性能相关部分用JavaScript实现。

Verstehen Sie kurz das Buffer-Modul in Node.js

Buffer所占用的内存不是通过V8分配,属于堆外内存。 由于V8垃圾回收性能影响,将常用的操作对象用更高效和专有的内存分配回收政策来管理是个不错的思路。

Buffer在Node进程启动时就已经价值,并且放在全局对象(global)上。所以使用buffer无需require引入

Buffer对象

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(&#39;12345678910&#39;)

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, &#39;开始我们的新路程&#39;, &#39;utf-8&#39;);
console.log(buf02.toString());  // 开始我们的新

BufferArray类型影响很大,可以访问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(&#39;我&#39;))  // 6:在第7个字节位置
console.log(buf02.slice(6, 9).toString())  // 我: 取得<Buffer e6 88 91>,转换后就是&#39;我&#39;

如果给字节赋值不是0255之间的整数,或者赋值时小数时,赋值小于0,将该值逐次加256.直到得到0255之间的整数。如果大于255,就逐次减去255。 如果是小数,舍去小数部分(不做四舍五入)

Buffer内存分配

Buffer对象的内存分配不是在V8的堆内存中,而是在Node的C++层面实现内存的申请。 因为处理大量的字节数据不能采用需要一点内存就向操作系统申请一点内存的方式。为此Node在内存上使用的是在C++层面申请内存,在JavaScript中分配内存的方式

Node采用了slab分配机制slab是以中动态内存管理机制,目前在一些*nix操作系统用中有广泛的应用,比如Linux

slab就是一块申请好的固定大小的内存区域,slab具有以下三种状态:

  • full:完全分配状态
  • partial:部分分配状态
  • empty:没有被分配状态

Node以8KB为界限来区分Buffer是大对象还是小对象

console.log(Buffer.poolSize);  // 8192

这个8KB的值就额是每个slab的大小值,在JavaScript层面,以它作为单位单元进行内存的分配

分配小buffer对象

如果指定Buffer大小小于8KB,Node会按照小对象方式进行分配

  1. 构造一个新的slab单元,目前slab处于empty空状态

Verstehen Sie kurz das Buffer-Modul in Node.js

  1. 构造小buffer对象1024KB,当前的slab会被占用1024KB,并且记录下是从这个slab的哪个位置开始使用的

Verstehen Sie kurz das Buffer-Modul in Node.js

  1. 这时再创建一个buffer对象,大小为3072KB。 构造过程会判断当前slab剩余空间是否足够,如果足够,使用剩余空间,并更新slab的分配状态。 3072KB空间被使用后,目前此slab剩余空间4096KB。

Verstehen Sie kurz das Buffer-Modul in Node.js

  1. 如果此时创建一个6144KB大小的buffer,当前slab空间不足,会构造新的slab
  2. Buffer ist ein Objekt wie Array, das hauptsächlich verwendet wird um Bytes zu manipulieren.

PufferstrukturVerstehen Sie kurz das Buffer-Modul in Node.js

Buffer ist ein typisches Modul, das JavaScript und C++ kombiniert. Die leistungsbezogenen Teile sind in C++ implementiert. Nicht leistungsrelevante Teile werden in JavaScript implementiert.

Verstehen Sie kurz das Buffer-Modul in Node.js🎜🎜Puffer Der belegte Speicher wird nicht über V8 zugewiesen und gehört zum Off-Heap-Speicher. Aufgrund der Leistungseinbußen der V8-Garbage Collection ist es eine gute Idee, häufig verwendete Operationsobjekte mit effizienteren und proprietären Speicherzuweisungs- und Recyclingrichtlinien zu verwalten. 🎜
🎜Buffer hat bereits einen Wert, wenn der Node-Prozess startet, und wird auf dem globalen Objekt (global) platziert. Daher ist es bei Verwendung des Puffers nicht erforderlich, einzuführen 🎜

🎜Pufferobjekt🎜

🎜Die Elemente des Pufferobjekts sind keine hexadezimalen zweistelligen Ziffern Zahlen, also 0-255 Numerische Werte 🎜
Buffer.alloc(1)
Buffer.alloc(8192)
🎜 können fill verwenden, um den Wert von buf zu füllen (der Standardwert ist die utf-8-Codierung). Überschreitet der gefüllte Wert den Puffer, wird er nicht geschrieben. 🎜
🎜Wenn die Pufferlänge größer als der Inhalt ist, wird er wiederholt gefüllt🎜
🎜Wenn Sie den zuvor gefüllten Inhalt löschen möchten, können Sie direkt fill()🎜
const fs = require(&#39;fs&#39;);

let rs = fs.createReadStream(&#39;./静夜思.txt&#39;, { flags:&#39;r&#39;});
let str = &#39;&#39;
rs.on(&#39;data&#39;, (chunk)=>{
    str += chunk;
})

rs.on(&#39;end&#39;, ()=>{
    console.log(str);
})
🎜Wenn der ausgefüllte Inhalt chinesisch ist, belegen unter dem Einfluss von utf-8 3 Elemente und Buchstaben und Satzzeichen halber Breite 1 Element. 🎜
fs.createReadStream(&#39;./静夜思.txt&#39;, { flags:&#39;r&#39;, highWaterMark: 11});
🎜Puffer wird stark vom Array-Typ beeinflusst. Sie können auf das Längenattribut zugreifen, um die Länge zu erhalten, Sie können auch über den Index auf das Element zugreifen Zeigen Sie auch die Elementposition über indexOf an. 🎜
fs.createReadStream(&#39;./静夜思.txt&#39;, { flags:&#39;r&#39;, highWaterMark: 11, encoding:&#39;utf-8&#39;});
🎜Wenn der dem Byte zugewiesene Wert keine Ganzzahl zwischen 0255 ist oder der zugewiesene Wert eine Dezimalzahl ist, ist der zugewiesene Wert kleiner als 0, add 256 nacheinander auf den Wert, bis Sie eine Ganzzahl zwischen 0 und 255 erhalten. Wenn es größer als 255 ist, subtrahieren Sie 255 nacheinander. Wenn es sich um eine Dezimalzahl handelt, verwerfen Sie den Dezimalteil (keine Rundung)🎜

🎜Pufferspeicherzuordnung🎜

🎜Die Speicherzuordnung des Puffers Das Objekt befindet sich nicht im Heap-Speicher von V8. Die Speicheranwendung wird auf der C++-Ebene von Node implementiert. Denn wenn Sie eine große Menge an Byte-Daten verarbeiten, können Sie beim Betriebssystem keinen Speicher beantragen, wenn Sie Speicher benötigen. Aus diesem Grund verwendet Node Speicheranwendungen auf C++-Ebene und weist Speicher in JavaScript zu. 🎜🎜Node übernimmt den Slab-Zuweisungsmechanismus, slab ist ein dynamischer Speicherverwaltungsmechanismus, der derzeit in einigen *nix-Betriebssystemen weit verbreitet ist, wie z. B. Linux🎜🎜 slab Ein Speicherbereich mit fester Größe, der beantragt wurde. Die Platte hat die folgenden drei Zustände: 🎜
  • voll: vollständig zugewiesener Status 🎜
  • teilweise: teilweise zugewiesener Status 🎜
  • Leer: Nicht zugewiesener Status 🎜
🎜Node verwendet 🎜8KB🎜 als Grenze, um zu unterscheiden, ob der Puffer ein großes oder ein kleines Objekt ist. 🎜
const { StringDecoder } = require(&#39;string_decoder&#39;);
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(&#39;------------------&#39;);

const decoder = new StringDecoder(&#39;utf8&#39;);
console.log(decoder.write(s1));
console.log(decoder.write(s2));
🎜Der 8-KB-Wert ist die Größe jeder Platte. Verwenden Sie ihn auf JavaScript-Ebene als Einheit zum Zuweisen von Speicher🎜

🎜Kleine Pufferobjekte zuweisen🎜

🎜Wenn Sie Buffer angeben, ist die Größe Wenn die Größe kleiner als 8 KB ist, weist Node sie gemäß der Small-Object-Methode zu.
  1. Erstellen Sie eine neue Platteneinheit. Derzeit befindet sich die Platte im leeren Zustand. Verstehen Sie kurz das Buffer-Modul in Node.js🎜
    1. Konstruieren Sie ein kleines buffer Das Code>-Objekt ist 1024 KB groß, und der Ort in diesem slab, von dem aus es verwendet wird, wird aufgezeichnet Verstehen Sie kurz das Buffer-Modul in Node.js🎜
      1. Erstellen Sie zu diesem Zeitpunkt ein weiteres Puffer-Objekt mit einer Größe von 3072 KB. Der Konstruktionsprozess bestimmt, ob der verbleibende Platz der aktuellen slab ausreicht. Wenn er ausreicht, verwenden Sie den verbleibenden Platz und aktualisieren Sie den Zuordnungsstatus von slab. Nachdem 3072 KB Speicherplatz genutzt wurden, beträgt der verbleibende Speicherplatz dieser Platte derzeit 4096 KB. 🎜🎜🎜Verstehen Sie kurz das Buffer-Modul in Node.js🎜
      2. Wenn zu diesem Zeitpunkt ein 6144 KB großer Puffer erstellt wird und der aktuelle Plattenspeicherplatz nicht ausreicht, wird eine neue Platte erstellt (dies geschieht). Der verbleibende Platz wird verschwendet) 🎜🎜🎜🎜🎜🎜Zum Beispiel im folgenden Beispiel: 🎜
        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的拼接

        buffer在使用场景中,通常是以一段段的方式进行传输。

        const fs = require(&#39;fs&#39;);
        
        let rs = fs.createReadStream(&#39;./静夜思.txt&#39;, { flags:&#39;r&#39;});
        let str = &#39;&#39;
        rs.on(&#39;data&#39;, (chunk)=>{
            str += chunk;
        })
        
        rs.on(&#39;end&#39;, ()=>{
            console.log(str);
        })

        以上是读取流的范例,data时间中获取到的chunk对象就是buffer对象。

        但是当输入流中有宽字节编码(一个字占多个字节)时,问题就会暴露。在str += chunk中隐藏了toString()操作。等价于str = str.toString() + chunk.toString()

        下面将可读流的每次读取buffer长度限制为11.

        fs.createReadStream(&#39;./静夜思.txt&#39;, { flags:&#39;r&#39;, highWaterMark: 11});

        输出得到:

        Verstehen Sie kurz das Buffer-Modul in Node.js

        上面出现了乱码,上面限制了buffer长度为11,对于任意长度的buffer而言,宽字节字符串都有可能存在被截断的情况,只不过buffer越长出现概率越低。

        encoding

        但是如果设置了encodingutf-8,就不会出现此问题了。

        fs.createReadStream(&#39;./静夜思.txt&#39;, { flags:&#39;r&#39;, highWaterMark: 11, encoding:&#39;utf-8&#39;});

        Verstehen Sie kurz das Buffer-Modul in Node.js

        原因: 虽然无论怎么设置编码,流的触发次数都是一样,但是在调用setEncoding时,可读流对象在内部设置了一个decoder对象。每次data事件都会通过decoder对象进行buffer到字符串的解码,然后传递给调用者。

        string_decoder 模块提供了用于将 Buffer 对象解码为字符串(以保留编码的多字节 UTF-8 和 UTF-16 字符的方式)的 API

        const { StringDecoder } = require(&#39;string_decoder&#39;);
        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(&#39;------------------&#39;);
        
        const decoder = new StringDecoder(&#39;utf8&#39;);
        console.log(decoder.write(s1));
        console.log(decoder.write(s2));

        Verstehen Sie kurz das Buffer-Modul in Node.js

        StringDecoder在得到编码之后,知道了宽字节字符串在utf-8编码下是以3个字节的方式存储的,所以第一次decoder.write只会输出前9个字节转码的字符,后两个字节会被保留在StringDecoder内部。

        Buffer与性能

        buffer在文件I/O和网络I/O中运用广泛,尤其在网络传输中,性能举足轻重。在应用中,通常会操作字符串,但是一旦在网络中传输,都需要转换成buffer,以进行二进制数据传输。 在web应用中,字符串转换到buffer是时时刻刻发生的,提高字符串到buffer的转换效率,可以很大程度地提高网络吞吐率。

        如果通过纯字符串的方式向客户端发送,性能会比发送buffer对象更差,因为buffer对象无须在每次响应时进行转换。通过预先转换静态内容为buffer对象,可以有效地减少CPU重复使用,节省服务器资源。

        可以选择将页面中动态和静态内容分离,静态内容部分预先转换为buffer的方式,使得性能得到提升。

        在文件的读取时,highWaterMark设置对性能影响至关重要。在理想状态下,每次读取的长度就是用户指定的highWaterMark

        highWaterMark大小对性能有两个影响的点:

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

        更多node相关知识,请访问:nodejs 教程!!

Das obige ist der detaillierte Inhalt vonVerstehen Sie kurz das Buffer-Modul in Node.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.cn. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen