首頁  >  文章  >  web前端  >  深入了解Node中的Buffer

深入了解Node中的Buffer

青灯夜游
青灯夜游轉載
2023-04-25 19:49:112298瀏覽

深入了解Node中的Buffer

在 Stream 篇結中,我們留下了一個問題,下述程式碼輸出的 chunk 是什麼東西?

深入了解Node中的Buffer

透過列印,我們發現 chunk 是 Buffer 對象,其中的元素是16進位的兩位數,也就是0~255的數值。 【相關教學推薦:nodejs影片教學程式設計教學

Untitled 1.png

#說明在Stream 中流動的資料就是Buffer,那以下就讓我們來探究一下Buffer 的真實面目!

? Node 中為什麼要引入Buffer?

最開始的時候JS 只在瀏覽器端運行,對於Unicode 編碼的字串容易處理,但是對於二進制和非Unicode 編碼的字串處理困難。而二進制是電腦最底層的資料格式,視訊/音訊/程式/網路包都是以二進位來儲存的。所以 Node 需要引入一個物件來操作二進位,因此 Buffer 誕生了,用於 TCP流/檔案系統等操作處理二進位位元組。

由於Buffer 在Node 中過於常用,所以在Node 啟動的時候已經引入了Buffer,無需使用require()

ArrayBuffer

是什麼

ArrayBuffer 是內存之中的一段二進位數據,本身不能夠操作內存,需要透過TypedArray 物件DataView 來操作。將緩衝區中的資料表示為特定的格式,並透過這些格式來讀寫緩衝區的內容,其部署了數組接口,可以使用數組的方式來操作資料

TypedArray 視圖

最常用的是TypeArray 視圖,用來讀寫簡單類型的ArrayBuffer,例如Uint8Array(無符號8位元整數)陣列視圖, Int16Array(16位元整數)陣列視圖

和Buffer 的關係

NodeJS 中的Buffer 類別其實是Uint8Array 的實作。

Buffer 結構

Buffer 是一個類似Array 的對象,但是它主要用於操作位元組

模組結構

Buffer 是JS 和C 結合的模組,性能部分都由C 實現,非性能部分都是JS 實現的Untitled 2.png

Buffer 所佔用的記憶體不是由V8 分配的,屬於堆外記憶體。

物件結構

Buffer 物件類似數組,其元素為16進位的兩位數,即0~255的數值

Untitled 3.png

#從這個例子能夠看出,不同字元在Buffer 中佔據的位元組是不一樣的,在UTF-8 編碼下,中文佔據3個字節,英文和半角標號佔用1個位元組

? 輸入的元素是小數/負數/超出255會發生什麼事?

Untitled 4.png

對於上述這種情況,Buffer 的處理為:

  • 給元素的賦值小於0, 就將該值逐次加256,直到得到一個0到255之間的整數
  • 如果得到的數值大於255,就逐次減256,直到得到0~255區間內的數值
  • 如果是小數,只保留整數部分

Buffer 裡面為什麼展示的是16進位

其實在記憶體儲存的依舊是二進制數,只是Buffer 在顯示這內存資料的時候採用了16進位

大小為2位元組的buffer,一共有16 bit ,例如是00000001 00100011,如果直接這樣顯示不太方便就轉成為了16進位<buffer></buffer>

Buffer 的建立

Buffer.alloc 與Buffer.allocUnsafe

# 建立固定大小的buffer

Buffer.alloc(size [, fill [, encoding]])

  • size 新 Buffer 的所需長度
  • fill 用於預先填入新 Buffer 的值。預設值: 0
  • encoding 如果 fill 是一個字串,則這是它的字元編碼。預設值: utf8

Untitled 5.png

Buffer.allocUnsafe(size)

分配一個大小為size 位元組的Buffer,allocUnsafe 執行速度比alloc 快,我們發現其結果並不像Buffer.alloc 那樣都初始化為00

Untitled 6.png

#當呼叫allocUnsafe 時分配的記憶體段尚未初始化,這樣分配記憶體速度很塊,但分配到的記憶體片段可能包含舊資料。如果使用的時候不覆寫這些舊資料就可能造成記憶體洩露,雖然速度快,盡量避免使用

Buffer 模組會預先分配一個內部的大小為 Buffer.poolSize 的Buffer 實例,作為快速分配的記憶體池,用於使用allocUnsafe 建立新的Buffer 實例

Buffer.from

根據內容直接建立Buffer

  • Buffer.from(string [, encoding] )
  • Buffer.from(array)
  • Buffer.from(buffer)

Untitled 7.png

##Buffer.allocUnsafe 的記憶體機制

為了有效地使用申請來的內存,Node.js 採用了slab 機制進行預先申請、事後分配,是一種動態的管理機制

使用 Buffer.alloc(size) 傳入一個指定的 size 就會申請一塊固定大小的記憶體區域,slab 有以下三種狀態

    full: 完全分配狀態
  • partial:部分分配狀態
  • # empty:沒有被指派狀態
Node.js 使用8 KB 為界限來區分是小物件還是大物件

Untitled 8.png

Buffer在創建的時候大小就已經被確定了且無法調整!

分配小物件

如果分配的物件小於8KB,Node 會以小物件的方式來進行分配

Buffer 的分配過程中主要使用一個局部變數 pool 作為中間處理對象,處於分配狀態的 slab 單元都指向它。以下是指派一個全新的slab 單元的操作,它將新申請的SlowBuffer 物件指向它

Untitled 9.png

一個slab 單元

Untitled 10.png

分配一個2KB 大小的Buffer

建立一個2KB 的buffer後,一個slab 單元記憶體如下:

Untitled 11.png

這個分配過程是由allocate方法完成

Untitled 12.png

當我們建立了一個2KB 的buffer 之後,目前slab 狀態為partial

再次建立buffer 的時候,會去判斷目前slab 剩餘空間是否足夠。如果足夠,使用剩餘空間,並更新slab 的分配狀態

如果slab 空間不夠,就會建構新的slab,原slab 中剩餘的空間造成浪費

分配大物件

如果有超過8KB 的buffer,直接會走到creatUnsafeBuffer 函數,分配一個slab 單元,這個slab 單元將會被這個大Buffer 物件獨佔

allocate 分配機制如圖

Untitled 13.png

#Buffer 的記憶體分配機制

Untitled 14.png

#Buffer 和字元編碼

透過使用字元編碼,可實現Buffer 實例與JavaScript 字串之間的相互轉換

Untitled 15.png

Node 中目前支援utf8、ucs2、utf16le、latin1、ascii、base64、hex、base64Url 八種編碼方式,具體實作

Untitled 16.png

#針對於每一種不同的編碼方案都會用實作一系列api,傳回會有不同的結果,Node.js 會根據傳入的encoding 來傳回不同的物件

Buffer 和字串的轉換

字串轉Buffer

主要透過上述講的Buffer.from 方法,預設的encoding 方式為utf-8

Buffer 轉字串

Untitled 17.png

#? 為什麼會出現亂碼呢?如何解決這個問題呢?

按著讀取來說,我們每次讀取的長度為4,chunk輸出如下

Untitled 18.png

#對於data = chunk等價於data = data.toString chunk.toString

#由於一個中文佔據三個字節,第一個chunk 中的第四個位元組會顯示亂碼,第二個chunk 的第一第二個位元組也無法形成文字等等,所以會展示亂碼問題

更多node相關知識,請造訪:nodejs 教學

以上是深入了解Node中的Buffer的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除