在没有出现Node.js以前,JavaScript仍是运行在浏览器端,对于处理Unicode编码的字符串数据很容易,可是对于处理二进制以及非Unicode编码的数据无能为力,可是对于Server端操做TCP以及文件I/O的处理是必须的。在Node.js里面提供了Buffer类处理二进制的数据,能够处理各类类型的数据。而且在Node.js里面一些重要模块net、http、fs中的数据传输以及处理都有Buffer的身影,由于一些基础的核心模块都要依赖Buffer,因此在node启动的时候,就已经加载了Buffer,咱们能够在全局下面直接使用Buffer。javascript
在v6.0以前建立Buffer对象直接使用new Buffer()
构造函数来建立对象实例,可是Buffer对内存的权限操做相比很大,能够直接捕获一些敏感信息,因此在v6.0之后,官方文档里面建议使用Buffer.from()
接口去建立Buffer对象。直接对比buffer.js的源码,看看二者有什么区别java
// Buffer构造函数的源码 function Buffer(arg, encodingOrOffset, length) { if (typeof arg === 'number') { if (typeof encodingOrOffset === 'string') { throw new Error( 'If encoding is specified then the first argument must be a string' ); } return Buffer.allocUnsafe(arg); } return Buffer.from(arg, encodingOrOffset, length); }
// Buffer.from函数的源码 Buffer.from = function(value, encodingOrOffset, length) { if (typeof value === 'number') throw new TypeError('"value" argument must not be a number'); if (isArrayBuffer(value) || isSharedArrayBuffer(value)) return fromArrayBuffer(value, encodingOrOffset, length); if (typeof value === 'string') return fromString(value, encodingOrOffset); return fromObject(value); };
由源码里面能够看到Buffer构造函数里面会判断第一个参数是否为数字类型而调用allocUnsafe
接口或者是直接调用from
接口去建立实例,而这两种建立对象的惟一区别就在于Buffer构造函数方式的第一个参数是数字类型,那么就是说,若是咱们使用构造函数方式建立时候第一个参数不传数字类型就和from
接口建立的逻辑是一致的,相对会更加安全。接下来看为啥若是第一个参数传递是数字的话会可能存在安全风险,若是第一个参数是数字,Buffer构造函数会去分配一个内存空间给到实例化的buffer使用,而调用allocUnsafe
接口去分类内存的时候,分配出来的内容空间是没有被初始化(数据没被重置),颇有可能会携带该缓存区以前的数据,若是缓存里面的内容是一些私钥、密码等敏感信息的话就可有可能被泄漏出去,下面举个例子:node
var password = 'thisIsMyPassword'; for( var i = 0, i < 100000; i++ ) { var buf = (new Buffer(200)).toString('ascii'); if (buf.indexOf(token) !== -1) { console.log('Found at i ' + i + ': ' + buf); } } // password内存申请的存储可能在new Buffer里面泄漏出去
而最初new Buffer()
API这样设计的会使得内存的分配很是快,由于不用每次都不用去初始化重置分配到的内容空间,虽然有必定的性能优点,可是也有必定的安全风险,下面是具体的性能耗时对比:c++
console.time('new'); for( var i = 0;i< 1000000;i++) { new Buffer(2000); } console.timeEnd('new'); console.time('alloc'); for( var i = 0;i< 1000000;i++) { Buffer.alloc(2000); } console.timeEnd('alloc'); // 运行结果,不初始化比初始化更快 // new: 1498ms // alloc: 2439ms
v6.0以后的版本都建议使用Buffer.alloc()
接口去分配内存,以及使用Buffer.from()
接口去建立Buffer实例,与此同时,新版也维持Buffer.allocUnsafe()
接口,可是语义上面已经说的明确,此外,在开启安全方面,咱们业务–zero-fill-buffers
来默认启用内存初始化,最后如下是总结:git
使用new Buffer()
构造函数建立Buffer对象实例并不是绝对的不安全 github
alloc
接口分配内存空间会初始化内存,不会泄漏旧缓存 数组
allocUnsafe
接口分配内存空间速度更优,但有数据安全风险 浏览器
Buffer可直接操做二进制数据类型,这必然要有二进制数据的载体,而在JavaScript里面已经实现了ArrayBuffer对象、TypedArray对象以及DataView对象在ES6的时候归入了ECMAScript规格里面。其实这些数据结构也被应用在浏览器端,例如File API、WebGL、Canvas、WebSockets等一些API底层都是二进制数据的通讯,查看node_buffer.cc
源码,Buffer在C++层面分配内存最终也是使用ArrayBuffer对象做为载体,如今先区分一下ArrayBuffer、TypedArray以及DataView三者的区别。缓存
ArrayBuffer对象 : 内存中一段原始的二进制数据,能够经过“视图”进行操做。安全
TypedArray对象 : 用来生成内存的视图,经过9个构造函数,能够生成9种数据格式的视图,好比Buffer里面就使用到Uint8Array(无符号8位整形)数组视图。
DataView对象 : 暂时与本文无关不作详细介绍。
简单点而言, 就是Buffer模块使用v8::ArrayBuffer分配一片内存,经过TypedArray中的v8::Uint8Array来去写数据 ,而说道Buffer的内存分配就不得不说Buffer的8KB的问题,对应buffer.js
源码里面的处理就是
Buffer.poolSize = 8 * 1024; function allocate(size) { if(size <= 0 ) return new FastBuffer(); if(size < Buffer.poolSize >>> 1 ) if(size > poolSize - poolOffset) createPool(); var b = allocPool.slice(poolOffset,poolOffset + size); poolOffset += size; alignPool(); return b } else { return createUnsafeBuffer(size); } }
源码直接看来就是以8KB做为界限,若是写入的数据大于8KB一半的话直接则直接去分配内存,若是小于4KB的话则从当前分配池里面判断是否够空间放下当前存储的数据,若是不够则从新去申请8KB的内存空间,把数据存储到新申请的空间里面,若是足够写入则直接写入数据到内存空间里面,下图为其内存分配策略。
如上图,若是当前存储了2KB的数据,后面要存储5KB大小数据的时候分配池判断所需内存空间大于4KB,则会去从新申请内存空间来存储5KB数据而且分配池的当前偏移指针也是指向新申请的内存空间,这时候就以前剩余的6KB(8KB-2KB)内存空间就会被搁置。至于为何会用8KB做为存储单元分配,这里还没进一步深究。
此外,Buffer单次的内存分配也有限制,而这个限制根据不一样操做系统而不一样,而这个限制能够看到node_buffer.h
里面
static const unsigned int kMaxLength = sizeof(int32_t) == sizeof(intptr_t) ? 0x3fffffff : 0x7fffffff;
对于32位的操做系统单次可最大分配的内存为1G,对于64位或者更高的为2G
Buffer与String二者均可以存储字符串类型的数据,可是,String与Buffer不一样,在内存分配上面,String直接使用v8堆存储,不用通过c++堆外分配内存,而且Google也对String进行优化,在实际的拼接测速对比中,String比Buffer快。可是Buffer的出现是为了处理二进制以及其余非Unicode编码的数据,因此在处理非utf8数据的时候须要使用到Buffer来处理。
ascii - 仅支持7位ASCII数据。
utf8 - 多字节编码的Unicode字符
utf16le - 2或4个字节,小端编码的Unicode字符
base64 - Base64字符串编码
binary - 二进制编码。
hex - 将每一个字节编码为两个十六进制字符。