Node中buffer模塊的作用是什么?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。

1. buffer的基本使用
在Node 6.0以前,直接使用new Buffer,但是這種方式存在兩個(gè)問題:
參數(shù)復(fù)雜: 內(nèi)存分配,還是內(nèi)存分配+內(nèi)容寫入,需要根據(jù)參數(shù)來確定
安全隱患: 分配到的內(nèi)存可能還存儲(chǔ)著舊數(shù)據(jù),這樣就存在安全隱患
// 本來只想申請(qǐng)一塊內(nèi)存,但是里面卻存在舊數(shù)據(jù) const buf1 = new Buffer(10) // <Buffer 90 09 70 6b bf 7f 00 00 50 3a> // 不小心,舊數(shù)據(jù)就被讀取出來了 buf1.toString() // '?\tpk?\u0000\u0000P:'
為了解決上述問題,Buffer提供了Buffer.from、Buffer.alloc、Buffer.allocUnsafe、Buffer.allocUnsafeSlow四個(gè)方法來申請(qǐng)內(nèi)存。
// 申請(qǐng)10個(gè)字節(jié)的內(nèi)存 const buf2 = Buffer.alloc(10) // <Buffer 00 00 00 00 00 00 00 00 00 00> // 默認(rèn)情況下,用0進(jìn)行填充 buf2.toString() //'\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000' // 上述操作就相當(dāng)于 const buf1 = new Buffer(10); buf.fill(0); buf.toString(); // '\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000'
2. buffer的結(jié)構(gòu)
buffer是一個(gè)典型的javascript與c++結(jié)合的模塊,其性能部分用c++實(shí)現(xiàn),非性能部分用javascript來實(shí)現(xiàn)。

下面看看buffer模塊的內(nèi)部結(jié)構(gòu):
exports.Buffer = Buffer; exports.SlowBuffer = SlowBuffer; exports.INSPECT_MAX_BYTES = 50; exports.kMaxLength = binding.kMaxLength;
buffer模塊提供了4個(gè)接口:
Buffer: 二進(jìn)制數(shù)據(jù)容器類,node啟動(dòng)時(shí)默認(rèn)加載
SlowBuffer: 同樣也是二進(jìn)制數(shù)據(jù)容器類,不過直接進(jìn)行內(nèi)存申請(qǐng)
INSPECT_MAX_BYTES: 限制bufObject.inspect()輸出的長度
kMaxLength: 一次性內(nèi)存分配的上限,大小為(2^31 - 1)
其中,由于Buffer經(jīng)常使用,所以node在啟動(dòng)的時(shí)候,就已經(jīng)加載了Buffer,而其他三個(gè),仍然需要使用require('buffer').***。
關(guān)于buffer的內(nèi)存申請(qǐng)、填充、修改等涉及性能問題的操作,均通過c++里面的node_buffer.cc來實(shí)現(xiàn):
// c++里面的node_buffer
namespace node {
bool zero_fill_all_buffers = false;
namespace Buffer {
...
}
}
NODE_MODULE_CONTEXT_AWARE_BUILTIN(buffer, node::Buffer::Initialize)3. 內(nèi)存分配的策略
Node中Buffer內(nèi)存分配太過常見,從系統(tǒng)性能考慮出發(fā),Buffer采用了如下的管理策略。

3.1 Buffer.from
Buffer.from(value, ...)用于申請(qǐng)內(nèi)存,并將內(nèi)容寫入剛剛申請(qǐng)的內(nèi)存中,value值是多樣的,Buffer是如何處理的呢?讓我們一起看看源碼:
Buffer.from = function(value, encodingOrOffset, length) {
if (typeof value === 'number')
throw new TypeError('"value" argument must not be a number');
if (value instanceof ArrayBuffer)
return fromArrayBuffer(value, encodingOrOffset, length);
if (typeof value === 'string')
return fromString(value, encodingOrOffset);
return fromObject(value);
};value可以分成三類:
ArrayBuffer的實(shí)例: ArrayBuffer是ES2015里面引入的,用于在瀏覽器端直接操作二進(jìn)制數(shù)據(jù),這樣Node就與ES2015關(guān)聯(lián)起來,同時(shí),新創(chuàng)建的Buffer與ArrayBuffer內(nèi)存是共享的
string: 該方法實(shí)現(xiàn)了將字符串轉(zhuǎn)變?yōu)锽uffer
Buffer/TypeArray/Array: 會(huì)進(jìn)行值的copy
3.1.1 ArrayBuffer的實(shí)例
Node v6與時(shí)俱進(jìn),將瀏覽器、node中對(duì)二進(jìn)制數(shù)據(jù)的操作關(guān)聯(lián)起來,同時(shí)二者會(huì)進(jìn)行內(nèi)存的共享。
var b = new ArrayBuffer(4);
var v1 = new Uint8Array(b);
var buf = Buffer.from(b)
console.log('first, typeArray: ', v1) // first, typeArray: Uint8Array [ 0, 0, 0, 0 ]
console.log('first, Buffer: ', buf) // first, Buffer: <Buffer 00 00 00 00>
v1[0] = 12
console.log('second, typeArray: ', v1) // second, typeArray: Uint8Array [ 12, 0, 0, 0 ]
console.log('second, Buffer: ', buf) // second, Buffer: <Buffer 0c 00 00 00>在上述操作中,對(duì)ArrayBuffer的操作,引起B(yǎng)uffer值的修改,說明二者在內(nèi)存上是同享的,再從源碼層面了解下這個(gè)過程:
// buffer.js Buffer.from(arrayBuffer, ...)進(jìn)入的分支:
function fromArrayBuffer(obj, byteOffset, length) {
byteOffset >>>= 0;
if (typeof length === 'undefined')
return binding.createFromArrayBuffer(obj, byteOffset);
length >>>= 0;
return binding.createFromArrayBuffer(obj, byteOffset, length);
}
// c++ 模塊中的node_buffer:
void CreateFromArrayBuffer(const FunctionCallbackInfo<Value>& args) {
...
Local<ArrayBuffer> ab = args[0].As<ArrayBuffer>();
...
Local<Uint8Array> ui = Uint8Array::New(ab, offset, max_length);
...
args.GetReturnValue().Set(ui);
}3.1.2 string
可以實(shí)現(xiàn)字符串與Buffer之間的轉(zhuǎn)換,同時(shí)考慮到操作的性能,采用了一些優(yōu)化策略避免頻繁進(jìn)行內(nèi)存分配:
function fromString(string, encoding) {
...
var length = byteLength(string, encoding);
if (length === 0)
return Buffer.alloc(0);
// 當(dāng)字符所需要的字節(jié)數(shù)大于4KB時(shí): 直接進(jìn)行內(nèi)存分配
if (length >= (Buffer.poolSize >>> 1))
return binding.createFromString(string, encoding);
// 當(dāng)字符所需字節(jié)數(shù)小于4KB: 借助allocPool先申請(qǐng)、后分配的策略
if (length > (poolSize - poolOffset))
createPool();
var actual = allocPool.write(string, poolOffset, encoding);
var b = allocPool.slice(poolOffset, poolOffset + actual);
poolOffset += actual;
alignPool();
return b;
}a. 直接內(nèi)存分配
當(dāng)字符串所需要的字節(jié)大于4KB時(shí),如何還從8KB的buffer pool中進(jìn)行申請(qǐng),那么就可能存在內(nèi)存浪費(fèi),例如:
poolSize - poolOffset < 4KB: 這樣就要重新申請(qǐng)一個(gè)8KB的pool,剛才那個(gè)pool剩余空間就會(huì)被浪費(fèi)掉
看看c++是如何進(jìn)行內(nèi)存分配的:
// c++
void CreateFromString(const FunctionCallbackInfo<Value>& args) {
...
Local<Object> buf;
if (New(args.GetIsolate(), args[0].As<String>(), enc).ToLocal(&buf))
args.GetReturnValue().Set(buf);
}b. 借助于pool管理
用一個(gè)pool來管理頻繁的行為,在計(jì)算機(jī)中是非常常見的行為,例如http模塊中,關(guān)于tcp連接的建立,就設(shè)置了一個(gè)tcp pool。
function fromString(string, encoding) {
...
// 當(dāng)字符所需字節(jié)數(shù)小于4KB: 借助allocPool先申請(qǐng)、后分配的策略
// pool的空間不夠用,重新分配8kb的內(nèi)存
if (length > (poolSize - poolOffset))
createPool();
// 在buffer pool中進(jìn)行分配
var actual = allocPool.write(string, poolOffset, encoding);
// 得到一個(gè)內(nèi)存的視圖view, 特殊說明: slice不進(jìn)行copy,僅僅創(chuàng)建view
var b = allocPool.slice(poolOffset, poolOffset + actual);
poolOffset += actual;
// 校驗(yàn)poolOffset是8的整數(shù)倍
alignPool();
return b;
}
// pool的申請(qǐng)
function createPool() {
poolSize = Buffer.poolSize;
allocPool = createBuffer(poolSize, true);
poolOffset = 0;
}
// node加載的時(shí)候,就會(huì)創(chuàng)建第一個(gè)buffer pool
createPool();
// 校驗(yàn)poolOffset是8的整數(shù)倍
function alignPool() {
// Ensure aligned slices
if (poolOffset & 0x7) {
poolOffset |= 0x7;
poolOffset++;
}
}3.1.3 Buffer/TypeArray/Array
可用從一個(gè)現(xiàn)有的Buffer、TypeArray或Array中創(chuàng)建Buffer,內(nèi)存不會(huì)共享,僅僅進(jìn)行值的copy。
var buf1 = new Buffer([1,2,3,4,5]); var buf2 = new Buffer(buf1); console.log(buf1); // <Buffer 01 02 03 04 05> console.log(buf2); // <Buffer 01 02 03 04 05> buf1[0] = 16 console.log(buf1); // <Buffer 10 02 03 04 05> console.log(buf2); // <Buffer 01 02 03 04 05>
上述示例就證明了buf1、buf2沒有進(jìn)行內(nèi)存的共享,僅僅是值的copy,再從源碼層面進(jìn)行分析:
function fromObject(obj) {
// 當(dāng)obj為Buffer時(shí)
if (obj instanceof Buffer) {
...
const b = allocate(obj.length);
obj.copy(b, 0, 0, obj.length);
return b;
}
// 當(dāng)obj為TypeArray或Array時(shí)
if (obj) {
if (obj.buffer instanceof ArrayBuffer || 'length' in obj) {
...
return fromArrayLike(obj);
}
if (obj.type === 'Buffer' && Array.isArray(obj.data)) {
return fromArrayLike(obj.data);
}
}
throw new TypeError(kFromErrorMsg);
}
// 數(shù)組或類數(shù)組,逐個(gè)進(jìn)行值的copy
function fromArrayLike(obj) {
const length = obj.length;
const b = allocate(length);
for (var i = 0; i < length; i++)
b[i] = obj[i] & 255;
return b;
}3.2 Buffer.alloc
Buffer.alloc用于內(nèi)存的分配,同時(shí)會(huì)對(duì)內(nèi)存的舊數(shù)據(jù)進(jìn)行覆蓋,避免安全隱患的產(chǎn)生。
Buffer.alloc = function(size, fill, encoding) {
...
if (size <= 0)
return createBuffer(size);
if (fill !== undefined) {
...
return typeof encoding === 'string' ?
createBuffer(size, true).fill(fill, encoding) :
createBuffer(size, true).fill(fill);
}
return createBuffer(size);
};
function createBuffer(size, noZeroFill) {
flags[kNoZeroFill] = noZeroFill ? 1 : 0;
try {
const ui8 = new Uint8Array(size);
Object.setPrototypeOf(ui8, Buffer.prototype);
return ui8;
} finally {
flags[kNoZeroFill] = 0;
}
}上述代碼有幾個(gè)需要注意的點(diǎn):
3.2.1 先申請(qǐng)后填充
alloc先通過createBuffer申請(qǐng)一塊內(nèi)存,然后再進(jìn)行填充,保證申請(qǐng)的內(nèi)存全部用fill進(jìn)行填充。
var buf = Buffer.alloc(10, 11); console.log(buf); // <Buffer 0b 0b 0b 0b 0b 0b 0b 0b 0b 0b>
3.2.2 flags標(biāo)示
flags用于標(biāo)識(shí)默認(rèn)的填充值是否為0,該值在javascript中設(shè)置,在c++中進(jìn)行讀取。
// js
const binding = process.binding('buffer');
const bindingObj = {};
...
binding.setupBufferJS(Buffer.prototype, bindingObj);
...
const flags = bindingObj.flags;
const kNoZeroFill = 0;// c++
void SetupBufferJS(const FunctionCallbackInfo<Value>& args) {
...
Local<Object> bObj = args[1].As<Object>();
...
bObj->Set(String::NewFromUtf8(env->isolate(), "flags"),
Uint32Array::New(array_buffer, 0, fields_count));
}3.2.3 Uint8Array
Uint8Array是ES2015 TypeArray中的一種,可以在瀏覽器中創(chuàng)建二進(jìn)制數(shù)據(jù),這樣就把瀏覽器、Node連接起來。
3.3 Buffer.allocUnSafe
Buffer.allocUnSafe與Buffer.alloc的區(qū)別在于,前者是從采用allocate的策略,嘗試從buffer pool中申請(qǐng)內(nèi)存,而buffer pool是不會(huì)進(jìn)行默認(rèn)值填充的,所以這種行為是不安全的。
Buffer.allocUnsafe = function(size) {
assertSize(size);
return allocate(size);
};3.4 Buffer.allocUnsafeSlow
Buffer.allocUnsafeSlow有兩個(gè)大特點(diǎn): 直接通過c++進(jìn)行內(nèi)存分配;不會(huì)進(jìn)行舊值填充。
Buffer.allocUnsafeSlow = function(size) {
assertSize(size);
return createBuffer(size, true);
};4. 結(jié)語
字符串與Buffer之間存在較大的差距,同時(shí)二者又存在編碼關(guān)系。通過Node,前端工程師已經(jīng)深入到網(wǎng)絡(luò)操作、文件操作等領(lǐng)域,對(duì)二進(jìn)制數(shù)據(jù)的操作就顯得非常重要,因此理解Buffer的諸多細(xì)節(jié)十分必要。
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司行業(yè)資訊頻道,感謝您對(duì)創(chuàng)新互聯(lián)建站的支持。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)建站www.chinadenli.net,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、建站服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。
本文標(biāo)題:Node中buffer模塊的作用是什么-創(chuàng)新互聯(lián)
文章源于:http://www.chinadenli.net/article40/dgecho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、App設(shè)計(jì)、移動(dòng)網(wǎng)站建設(shè)、網(wǎng)站營銷、手機(jī)網(wǎng)站建設(shè)、企業(yè)網(wǎng)站制作
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容