buffer.concat引出的bug
by snoopyxdy
at 2013-03-30 15:42:10
original http://snoopyxdy.blog.163.com/blog/static/601174402013230102139585
具体报错的位置在:buffer.js:523
throw new RangeError('targetStart out of bounds');
其中rec_ary就是存放buffer的数组。var buf = Buffer.concat(rec_ary, rec_ary.length);
Class Method: Buffer.concat(list, [totalLength])#list Array List of Buffer objects to concattotalLength Number Total length of the buffers when concatenated...If totalLength is not provided, it is read from the buffers in the list. However, this adds an additional loop to the function, so it is faster to provide the length explicitly.
Buffer.concat = function(list, length) {... //省略if (typeof length !== 'number') {length = 0;for (var i = 0; i < list.length; i++) {var buf = list[i];length += buf.length;}}...//省略}
var bufAry = []var bufLen = 0;for(var i=0;i<5;i++){bufAry[i] = new Buffer("str is "+ i + '\n');bufLen += bufAry[i].length}console.log(bufLen)var newBuf = Buffer.concat(bufAry,bufLen)console.log(newBuf.toString())
所以希望今后大家用到buffer.concat方法时要注意以下,第二个参数务必传递buffers的总长度,当然你也可以偷懒省略第二个参数,这样也不会有问题。45
str is 0
str is 1
str is 2
str is 3
str is 4
Buffer.poolSize = 8 * 1024;var pool;function allocPool() {pool = new SlowBuffer(Buffer.poolSize);pool.used = 0;}
function Buffer(subject, encoding, offset) { //buffer类... //省略以上是一些参数的初始化,switch判断等等if (this.length > Buffer.poolSize) { //this.length就是之前初始化的本buffer实例需要分配的内存bytes空间,跟8KB进行比较// Big buffer, just alloc one.this.parent = new SlowBuffer(this.length); //如果大于8KB则使用c++的api重新为它分配存储空间,并且将返回的实例赋值给this.parent,这样我们就知道了此buffer实例保存在了哪块内存中this.offset = 0; //因为这块内存是此buffer实例第一个独享的,所以记录其偏移为0} else if (this.length > 0) { //如果此buffer实例大于0但是小于8KB ,node认为是小的buffer// Small buffer.if (!pool || pool.length - pool.used < this.length) allocPool(); //如果当前8KB内存池不够存储了,则重新分配一个slowbuffer让此buffer实例存储this.parent = pool; //将当前分配的内存buffer池赋值给 this.parent 方便之后的读取和剪切等this.offset = pool.used; //将偏移赋值给此buffer实例pool.used += this.length; //并且更新此buffer池的使用byteif (pool.used & 7) pool.used = (pool.used + 8) & ~7;//将pool.used按位与7,如果pool.used是8或8的倍数,则表达式pool.used & 7是false,否则为true// (pool.used + 8) & ~7; 这里将返回大于pool.used最近的8的倍数//比如我们pool.used的大小为9byte,则执行这行代码之后,pool.used会调整为16,6byte的内存浪费了,这也是很多文章建议我们申请小于8KB的内存的时候最好是8的倍数,避免造成浪费} else { //如果是0长度的buffer,所以所有长度0的buffer都是 var zeroBuffer = new SlowBuffer(0);// Zero-length bufferthis.parent = zeroBuffer;this.offset = 0;}... //处理一些数据,包括调用wrtie将buffer写入SlowBuffer.makeFastBuffer(this.parent, this, this.offset, this.length);//最后将这些参数通过slowbuffer的makeFastBuffer将内存地址和js的buffer实例做好关联引用}
static v8::Handle<v8::Value> MakeFastBuffer(const v8::Arguments &args);
Handle<Value> Buffer::MakeFastBuffer(const Arguments &args) {
HandleScope scope;
if (!Buffer::HasInstance(args[0])) {
return ThrowTypeError("First argument must be a Buffer"); //判断第一个函数是否是buffer类的实例
}
Buffer *buffer = ObjectWrap::Unwrap<Buffer>(args[0]->ToObject()); //创建一个Buffer指针指向this.parent
Local<Object> fast_buffer = args[1]->ToObject();; //定义V8的local<object>类型的fast_buffer指向this
uint32_t offset = args[2]->Uint32Value(); //将偏移量和this.length保存成C++的uint32
uint32_t length = args[3]->Uint32Value();
if (offset > buffer->length_) { //如果偏移量比this.parent总长度还要长,则抛出异常
return ThrowRangeError("offset out of range");
}
if (offset + length > buffer->length_) { //如果偏移量加上本buffer的长度大于this.parent的总长度,则抛出异常
return ThrowRangeError("length out of range");
}
// Check for wraparound. Safe because offset and length are unsigned.
if (offset + length < offset) { //如果偏移量或者length加起来小于偏移量,则抛出异常,一般不会出现
return ThrowRangeError("offset or length out of range");
}
fast_buffer->SetIndexedPropertiesToExternalArrayData(buffer->data_ + offset,
kExternalUnsignedByteArray,
length);//最后调用v8接口的SetIndexedPropertiesToExternalArrayData方法将数据与js的对象建立起引用关系
return Undefined();
}
var os = require('os');
var leak_buf_ary = [];
var show_memory_usage = function(){ //打印系统空闲内存
console.log('free mem : ' + Math.ceil(os.freemem()/(1024*1024)) + 'mb');
}
var do_buf_leak = function(){
var leak_char = 'l'; //泄露的几byte字符
var loop = 100000;//10万次
var buf1_ary = []
while(loop--){
buf1_ary.push(new Buffer(4096)); //申请buf1,占用4096byte空间,会得到自动释放
//申请buf2,占用几byte空间,将其引用保存在外部数据,不会自动释放
//*******
leak_buf_ary.push(new Buffer(loop+leak_char));
//*******
}
console.log("before gc")
show_memory_usage();
buf1_ary = null;
return;
}
console.log("process start")
show_memory_usage()
do_buf_leak();
var j =10000;
setInterval(function(){
console.log("after gc")
show_memory_usage()
},1000*60)
process startfree mem : 5362mbbefore gcfree mem : 5141mbafter gcfree mem : 5163mbafter gcfree mem : 5151mbafter gcfree mem : 5148mbafter gcfree mem : 5556mb
process start free mem : 5692mb before gc free mem : 4882mb after gc free mem : 4848mb after gc free mem : 4842mb after gc free mem : 4843mb after gc free mem : 4816mb after gc free mem : 4822mb after gc free mem : 4816mb after gc free mem : 4809mb after gc free mem : 4810mb after gc free mem : 4831mb after gc free mem : 4830mb