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.
This commit is contained in:
parent
3a2f273bd7
commit
456942a920
@ -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.
|
||||
Returns an un-pooled `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.
|
||||
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.
|
||||
|
@ -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');
|
||||
|
||||
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…
x
Reference in New Issue
Block a user