Reduce the default initial capacity of ChannelOutboundBuffer

Motivation:

ChannelOutboundBuffer is basically a circular array queue of its entry
objects.  Once an entry is created in the array, it is never nulled out
to reduce the allocation cost.

However, because it is a circular queue, the array almost always ends up
with as many entry instances as the size of the array, regardless of the
number of pending writes.

At worst case, a channel might have only 1 pending writes at maximum
while creating 32 entry objects, where 32 is the initial capacity of the
array.

Modifications:

- Reduce the initial capacity of the circular array queue to 4.
- Make the initial capacity of the circular array queue configurable

Result:

We spend 4 times less memory for entry objects under certain
circumstances.
This commit is contained in:
Trustin Lee 2014-07-22 13:28:02 -07:00
parent ed304b42f5
commit 08c87c6256
2 changed files with 11 additions and 7 deletions

View File

@ -48,14 +48,18 @@ public final class ChannelOutboundBuffer {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelOutboundBuffer.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelOutboundBuffer.class);
private static final int INITIAL_CAPACITY = 32; private static final int INITIAL_CAPACITY =
SystemPropertyUtil.getInt("io.netty.outboundBufferInitialCapacity", 4);
private static final int threadLocalDirectBufferSize; private static final int threadLocalDirectBufferSize =
SystemPropertyUtil.getInt("io.netty.threadLocalDirectBufferSize", 64 * 1024);
static { static {
threadLocalDirectBufferSize = SystemPropertyUtil.getInt("io.netty.threadLocalDirectBufferSize", 64 * 1024); if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.outboundBufferInitialCapacity: {}", INITIAL_CAPACITY);
logger.debug("-Dio.netty.threadLocalDirectBufferSize: {}", threadLocalDirectBufferSize); logger.debug("-Dio.netty.threadLocalDirectBufferSize: {}", threadLocalDirectBufferSize);
} }
}
private static final Recycler<ChannelOutboundBuffer> RECYCLER = new Recycler<ChannelOutboundBuffer>() { private static final Recycler<ChannelOutboundBuffer> RECYCLER = new Recycler<ChannelOutboundBuffer>() {
@Override @Override

View File

@ -34,7 +34,7 @@ public class ChannelOutboundBufferTest {
ChannelOutboundBuffer buffer = ChannelOutboundBuffer.newInstance(channel); ChannelOutboundBuffer buffer = ChannelOutboundBuffer.newInstance(channel);
assertEquals(0, buffer.nioBufferCount()); assertEquals(0, buffer.nioBufferCount());
ByteBuffer[] buffers = buffer.nioBuffers(); ByteBuffer[] buffers = buffer.nioBuffers();
assertEquals(32, buffers.length); assertNotNull(buffers);
for (ByteBuffer b: buffers) { for (ByteBuffer b: buffers) {
assertNull(b); assertNull(b);
} }
@ -49,7 +49,7 @@ public class ChannelOutboundBufferTest {
ChannelOutboundBuffer buffer = ChannelOutboundBuffer.newInstance(channel); ChannelOutboundBuffer buffer = ChannelOutboundBuffer.newInstance(channel);
assertEquals(0, buffer.nioBufferCount()); assertEquals(0, buffer.nioBufferCount());
ByteBuffer[] buffers = buffer.nioBuffers(); ByteBuffer[] buffers = buffer.nioBuffers();
assertEquals(32, buffers.length); assertNotNull(buffers);
for (ByteBuffer b: buffers) { for (ByteBuffer b: buffers) {
assertNull(b); assertNull(b);
} }
@ -65,7 +65,7 @@ public class ChannelOutboundBufferTest {
} }
buffer.addFlush(); buffer.addFlush();
buffers = buffer.nioBuffers(); buffers = buffer.nioBuffers();
assertEquals(32, buffers.length); assertNotNull(buffers);
assertEquals("Should still be 0 as not flushed yet", 1, buffer.nioBufferCount()); assertEquals("Should still be 0 as not flushed yet", 1, buffer.nioBufferCount());
for (int i = 0; i < buffers.length; i++) { for (int i = 0; i < buffers.length; i++) {
if (i == 0) { if (i == 0) {