Browse Source

buffer: reimplement Buffer pools

While the new Buffer implementation is much faster we still have the
necessity of using Buffer pools. This is undesirable because it may
still lead to unwanted memory retention, but for the time being this is
the best solution.

Because of this re-introduction, and since there is no more SlowBuffer
type, the SlowBuffer method has been re-purposed to return a non-pooled
Buffer instance. This will be helpful for developers to store data for
indeterminate lengths of time without introducing a memory leak.

Another change to Buffer pools was that they are only allocated if the
requested chunk is < poolSize / 2. This was done because allocations are
much quicker now, and it's a better use of the pool.
v0.11.3-release
Trevor Norris 12 years ago
parent
commit
456942a920
  1. 30
      doc/api/buffer.markdown
  2. 36
      lib/buffer.js

30
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.

36
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) {

Loading…
Cancel
Save