怎么理解Node.js中的Buffer模塊

這篇文章主要講解了“怎么理解Node.js中的Buffer模塊”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“怎么理解Node.js中的Buffer模塊”吧!

10年積累的網(wǎng)站建設(shè)、成都網(wǎng)站制作經(jīng)驗(yàn),可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先建設(shè)網(wǎng)站后付款的網(wǎng)站建設(shè)流程,更有楊浦免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

怎么理解Node.js中的Buffer模塊

理解Buffer

JavaScript對于字符串的操作十分友好

Buffer是一個像Array的對象,主要用于操作字節(jié)。

Buffer結(jié)構(gòu)

Buffer是一個典型的JavaScript和C++結(jié)合的模塊,將性能相關(guān)部分用C++實(shí)現(xiàn),將非性能相關(guān)部分用JavaScript實(shí)現(xiàn)。

怎么理解Node.js中的Buffer模塊

Buffer所占用的內(nèi)存不是通過V8分配,屬于堆外內(nèi)存。 由于V8垃圾回收性能影響,將常用的操作對象用更高效和專有的內(nèi)存分配回收政策來管理是個不錯的思路。

Buffer在Node進(jìn)程啟動時就已經(jīng)價(jià)值,并且放在全局對象(global)上。所以使用buffer無需require引入

Buffer對象

Buffer對象的元素未16進(jìn)制的兩位數(shù),即0-255的數(shù)值

let buf01 = Buffer.alloc(8);
console.log(buf01);  // <Buffer 00 00 00 00 00 00 00 00>

可以使用fill填充buf的值(默認(rèn)為utf-8編碼),如果填充的值超過buffer,將不會被寫入。

如果buffer長度大于內(nèi)容,則會反復(fù)填充

如果想要清空之前填充的內(nèi)容,可以直接fill()

buf01.fill('12345678910')

console.log(buf01);   // <Buffer 31 32 33 34 35 36 37 38>
console.log(buf01.toString()); // 12345678

如果填入的內(nèi)容是中文,在utf-8的影響下,中文字會占用3個元素,字母和半角標(biāo)點(diǎn)符號占用1個元素。

let buf02 = Buffer.alloc(18, '開始我們的新路程', 'utf-8');
console.log(buf02.toString());  // 開始我們的新

BufferArray類型影響很大,可以訪問length屬性得到長度,也可以通過下標(biāo)訪問元素,也可以通過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字節(jié)
console.log(buf02[6])  // 230: e6 轉(zhuǎn)換后就是 230
console.log(buf02.indexOf('我'))  // 6:在第7個字節(jié)位置
console.log(buf02.slice(6, 9).toString())  // 我: 取得<Buffer e6 88 91>,轉(zhuǎn)換后就是'我'

如果給字節(jié)賦值不是0255之間的整數(shù),或者賦值時小數(shù)時,賦值小于0,將該值逐次加256.直到得到0255之間的整數(shù)。如果大于255,就逐次減去255。 如果是小數(shù),舍去小數(shù)部分(不做四舍五入)

Buffer內(nèi)存分配

Buffer對象的內(nèi)存分配不是在V8的堆內(nèi)存中,而是在Node的C++層面實(shí)現(xiàn)內(nèi)存的申請。 因?yàn)樘幚泶罅康淖止?jié)數(shù)據(jù)不能采用需要一點(diǎn)內(nèi)存就向操作系統(tǒng)申請一點(diǎn)內(nèi)存的方式。為此Node在內(nèi)存上使用的是在C++層面申請內(nèi)存,在JavaScript中分配內(nèi)存的方式

Node采用了slab分配機(jī)制,slab是以中動態(tài)內(nèi)存管理機(jī)制,目前在一些*nix操作系統(tǒng)用中有廣泛的應(yīng)用,比如Linux

slab就是一塊申請好的固定大小的內(nèi)存區(qū)域,slab具有以下三種狀態(tài):

  • full:完全分配狀態(tài)

  • partial:部分分配狀態(tài)

  • empty:沒有被分配狀態(tài)

Node以8KB為界限來區(qū)分Buffer是大對象還是小對象

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

這個8KB的值就額是每個slab的大小值,在JavaScript層面,以它作為單位單元進(jìn)行內(nèi)存的分配

分配小buffer對象

如果指定Buffer大小小于8KB,Node會按照小對象方式進(jìn)行分配

  1. 構(gòu)造一個新的slab單元,目前slab處于empty空狀態(tài)

怎么理解Node.js中的Buffer模塊

  1. 構(gòu)造小buffer對象1024KB,當(dāng)前的slab會被占用1024KB,并且記錄下是從這個slab的哪個位置開始使用的

怎么理解Node.js中的Buffer模塊

  1. 這時再創(chuàng)建一個buffer對象,大小為3072KB。 構(gòu)造過程會判斷當(dāng)前slab剩余空間是否足夠,如果足夠,使用剩余空間,并更新slab的分配狀態(tài)。 3072KB空間被使用后,目前此slab剩余空間4096KB。

怎么理解Node.js中的Buffer模塊

  1. 如果此時創(chuàng)建一個6144KB大小的buffer,當(dāng)前slab空間不足,會構(gòu)造新的slab(這會造成原slab剩余空間浪費(fèi))

怎么理解Node.js中的Buffer模塊

比如下面的例子中:

Buffer.alloc(1)
Buffer.alloc(8192)

第一個slab中只會存在1字節(jié)的buffer對象,而后一個buffer對象會構(gòu)建一個新的slab存放

由于一個slab可能分配給多個Buffer對象使用,只有這些小buffer對象在作用域釋放并都可以回收時,slab的空間才會被回收。 盡管只創(chuàng)建1字節(jié)的buffer對象,但是如果不釋放,實(shí)際是8KB的內(nèi)存都沒有釋放

小結(jié):

真正的內(nèi)存是在Node的C++層面提供,JavaScript層面只是使用。當(dāng)進(jìn)行小而頻繁的Buffer操作時,采用slab的機(jī)制進(jìn)行預(yù)先申請和時候分配,使得JavaScript到操作系統(tǒng)之間不必有過多的內(nèi)存申請方面的系統(tǒng)調(diào)用。 對于大塊的buffer,直接使用C++層面提供的內(nèi)存即可,無需細(xì)膩的分配操作。

Buffer的拼接

buffer在使用場景中,通常是以一段段的方式進(jìn)行傳輸。

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對象。

但是當(dāng)輸入流中有寬字節(jié)編碼(一個字占多個字節(jié))時,問題就會暴露。在str += chunk中隱藏了toString()操作。等價(jià)于str = str.toString() + chunk.toString()。

下面將可讀流的每次讀取buffer長度限制為11.

fs.createReadStream('./靜夜思.txt', { flags:'r', highWaterMark: 11});

輸出得到:

怎么理解Node.js中的Buffer模塊

上面出現(xiàn)了亂碼,上面限制了buffer長度為11,對于任意長度的buffer而言,寬字節(jié)字符串都有可能存在被截?cái)嗟那闆r,只不過buffer越長出現(xiàn)概率越低。

encoding

但是如果設(shè)置了encodingutf-8,就不會出現(xiàn)此問題了。

fs.createReadStream('./靜夜思.txt', { flags:'r', highWaterMark: 11, encoding:'utf-8'});

怎么理解Node.js中的Buffer模塊

原因:雖然無論怎么設(shè)置編碼,流的觸發(fā)次數(shù)都是一樣,但是在調(diào)用setEncoding時,可讀流對象在內(nèi)部設(shè)置了一個decoder對象。每次data事件都會通過decoder對象進(jìn)行buffer到字符串的解碼,然后傳遞給調(diào)用者。

string_decoder 模塊提供了用于將 Buffer 對象解碼為字符串(以保留編碼的多字節(jié) 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));

怎么理解Node.js中的Buffer模塊

StringDecoder在得到編碼之后,知道了寬字節(jié)字符串在utf-8編碼下是以3個字節(jié)的方式存儲的,所以第一次decoder.write只會輸出前9個字節(jié)轉(zhuǎn)碼的字符,后兩個字節(jié)會被保留在StringDecoder內(nèi)部。

Buffer與性能

buffer在文件I/O和網(wǎng)絡(luò)I/O中運(yùn)用廣泛,尤其在網(wǎng)絡(luò)傳輸中,性能舉足輕重。在應(yīng)用中,通常會操作字符串,但是一旦在網(wǎng)絡(luò)中傳輸,都需要轉(zhuǎn)換成buffer,以進(jìn)行二進(jìn)制數(shù)據(jù)傳輸。 在web應(yīng)用中,字符串轉(zhuǎn)換到buffer是時時刻刻發(fā)生的,提高字符串到buffer的轉(zhuǎn)換效率,可以很大程度地提高網(wǎng)絡(luò)吞吐率。

如果通過純字符串的方式向客戶端發(fā)送,性能會比發(fā)送buffer對象更差,因?yàn)閎uffer對象無須在每次響應(yīng)時進(jìn)行轉(zhuǎn)換。通過預(yù)先轉(zhuǎn)換靜態(tài)內(nèi)容為buffer對象,可以有效地減少CPU重復(fù)使用,節(jié)省服務(wù)器資源。

可以選擇將頁面中動態(tài)和靜態(tài)內(nèi)容分離,靜態(tài)內(nèi)容部分預(yù)先轉(zhuǎn)換為buffer的方式,使得性能得到提升。

在文件的讀取時,highWaterMark設(shè)置對性能影響至關(guān)重要。在理想狀態(tài)下,每次讀取的長度就是用戶指定的highWaterMark。

highWaterMark大小對性能有兩個影響的點(diǎn):

  • 對buffer內(nèi)存的分配和使用有一定影響

  • 設(shè)置過小,可能導(dǎo)致系統(tǒng)調(diào)用次數(shù)過多

感謝各位的閱讀,以上就是“怎么理解Node.js中的Buffer模塊”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對怎么理解Node.js中的Buffer模塊這一問題有了更深刻的體會,具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識點(diǎn)的文章,歡迎關(guān)注!

新聞名稱:怎么理解Node.js中的Buffer模塊
標(biāo)題鏈接:http://muchs.cn/article26/iehjcg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、網(wǎng)站設(shè)計(jì)、面包屑導(dǎo)航、商城網(wǎng)站虛擬主機(jī)、動態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司