diff --git a/doc/api/buffer.markdown b/doc/api/buffer.markdown index 335ac4dcc3..d880911f0d 100644 --- a/doc/api/buffer.markdown +++ b/doc/api/buffer.markdown @@ -694,8 +694,28 @@ Note that this is a property on the buffer module returned by ## Class: SlowBuffer -Deprecated. SlowBuffer now returns an instance of Buffer. - -In order to avoid the overhead of allocating many C++ Buffer objects for -small blocks of memory in the lifetime of a server, Node allocates memory -in 8Kb (8192 byte) chunks. This is now handled by Smalloc. +Returns an un-pooled `Buffer`. + +In order to avoid the garbage collection overhead of creating many individually +allocated Buffers, by default allocations under 4KB are sliced from a single +larger allocated object. This approach improves both performance and memory +usage since v8 does not need to track and cleanup as many `Persistent` objects. + +In the case where a developer may need to retain a small chunk of memory from a +pool for an indeterminate amount of time it may be appropriate to create an +un-pooled Buffer instance using SlowBuffer and copy out the relevant bits. + + // need to keep around a few small chunks of memory + var store = []; + + socket.on('readable', function() { + var data = socket.read(); + // allocate for retained data + var sb = new SlowBuffer(10); + // copy the data into the new allocation + data.copy(sb, 0, 0, 10); + store.push(sb); + }); + +Though this should used sparingly and only be a last resort *after* a developer +has actively observed undue memory retention in their applications. diff --git a/lib/buffer.js b/lib/buffer.js index a5abf368af..e0ce4c545e 100644 --- a/lib/buffer.js +++ b/lib/buffer.js @@ -27,13 +27,25 @@ var sliceOnto = smalloc.sliceOnto; var kMaxLength = smalloc.kMaxLength; exports.Buffer = Buffer; -// backwards compatibility (DEPRECATE) -exports.SlowBuffer = Buffer; +exports.SlowBuffer = SlowBuffer; exports.INSPECT_MAX_BYTES = 50; // add methods to Buffer prototype buffer.setupBufferJS(Buffer); +Buffer.poolSize = 8 * 1024; +var poolSize = Buffer.poolSize; +var poolOffset = 0; +var allocPool = alloc({}, poolSize); + + +function createPool() { + poolSize = Buffer.poolSize; + allocPool = alloc({}, poolSize); + poolOffset = 0; +} + + function Buffer(subject, encoding) { if (!(this instanceof Buffer)) return new Buffer(subject, encoding); @@ -67,7 +79,17 @@ function Buffer(subject, encoding) { if (this.length > kMaxLength) throw new RangeError('length > kMaxLength'); - alloc(this, this.length); + if (this.length < Buffer.poolSize / 2 && this.length > 0) { + if (this.length > poolSize - poolOffset) + createPool(); + this.parent = sliceOnto(allocPool, + this, + poolOffset, + poolOffset + this.length); + poolOffset += this.length; + } else { + alloc(this, this.length); + } if (type !== 'number') { if (type === 'string') { @@ -84,6 +106,14 @@ function Buffer(subject, encoding) { } +function SlowBuffer(length) { + length = ~~length; + var b = new Buffer(undefined, length); + alloc(b, length); + return b; +} + + // Static methods Buffer.isBuffer = function isBuffer(b) {