この記事では、Node.js のバッファを理解し、バッファ構造、バッファ メモリ割り当て、バッファ スプライシングなどについて説明します。皆さんのお役に立てれば幸いです。
JavaScript文字列操作に非常に適しています
は Array
のようなオブジェクトで、主にバイトの操作に使用されます。
は、JavaScript と C を組み合わせた代表的なモジュールです。パフォーマンスに関連する部分は C で実装され、それ以外の部分は C で実装されます。パフォーマンス関連の部分は C で実装されています。一部は JavaScript で実装されています。
#Buffer によって占有されるメモリは V8 を通じて割り当てられず、オフヒープ メモリに属します。 V8 ガベージ コレクションはパフォーマンスに影響を与えるため、より効率的で独自のメモリ割り当てとリサイクル ポリシーを使用して、一般的に使用される操作オブジェクトを管理することをお勧めします。
バッファは、ノード プロセスの開始時にすでに値が設定されており、グローバル オブジェクト (グローバル) に配置されます。Buffer オブジェクト
let buf01 = Buffer.alloc(8); console.log(buf01); // <Buffer 00 00 00 00 00 00 00 00>
の値です。
fill を使用して buf の値を埋めることができます (デフォルトは utf-8
エンコードです)。値がバッファを超えると、書き込まれません。
以前に埋められたコンテンツをクリアしたい場合は、直接fill を実行できます。 ()
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">buf01.fill(&#39;12345678910&#39;)
console.log(buf01); // <Buffer 31 32 33 34 35 36 37 38>
console.log(buf01.toString()); // 12345678</pre><div class="contentsignin">ログイン後にコピー</div></div>
記入内容が中国語の場合、
の影響で、3要素に漢字が入り、文字と半角句読点が入ります。 1要素を占有します。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">let buf02 = Buffer.alloc(18, &#39;开始我们的新路程&#39;, &#39;utf-8&#39;);
console.log(buf02.toString()); // 开始我们的新</pre><div class="contentsignin">ログイン後にコピー</div></div>
は Array type
によって大きく影響されます。長さ属性にアクセスして長さを取得したり、添字を使用して要素にアクセスしたりすることもできます。また、indexOf を通じて要素の位置を表示することもできます。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">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;</pre><div class="contentsignin">ログイン後にコピー</div></div>
バイトに割り当てられた値が 0
255 の整数の間の値を取得します。 255 より大きい場合は、255 を 1 つずつ減算します。小数の場合は、小数部分を切り捨てます (四捨五入なし)
バッファ メモリ割り当てオブジェクトのメモリ割り当ては、 V8 ヒープ内 メモリ内では、メモリ アプリケーションはノードの C レベルで実装されます。大量のバイト データを処理する場合、メモリが必要なときにオペレーティング システムからメモリを申請できないためです。このため、Node は C レベルのメモリを使用して JavaScript
で スラブ割り当てメカニズムを採用します
, slab
は動的メモリ管理メカニズムであり、現在 Linux
slab
などの一部の
オペレーティング システムで広く使用されています。スラブには次の 3 つの状態があります:
を使用します。console.log(Buffer.poolSize); // 8192
#小さなバッファ オブジェクトを割り当てます
に従ってサイズを割り当てます。新しいスラブ ユニットを構築します。スラブは現在空の状態です。
1024KB の小さな
buffer は 1024KB で占有されます。レコードはどこからのものです この
slab はどこから使用され始めますか?
この時点で、
を作成しますサイズが 3072KB のbuffer の残りのスペースが十分であるかどうかが判断され、十分な場合は、残りのスペースを使用して、
slab の割り当てステータスを更新します。 3072KB のスペースが使用された後、このスラブの残りのスペースは現在 4096KB です。
この時点で 6144KB のサイズの
buffer (これにより、元のスラブの残りのスペースが無駄になります)
たとえば、次のようになります。例: ###
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 中国語 Web サイトの他の関連記事を参照してください。