Reduce memory footprint of DefaultChannelPipeline

Motivation:

If you need to handle a lot of concurrent connections (1M+) the memory footprint can be problem.

Modifications:

- Lazy create the IdentityHashMap that holds the EventExecutor mappings as this is not needed by most users anyway
- Use a sane initial capacity when creating the IdentityHashMap

Result:

Smaller memory footprint of DefaultChannelPipeline
This commit is contained in:
Norman Maurer 2015-11-15 08:30:04 -08:00
parent 19950f89e4
commit 01da38d21a
3 changed files with 34 additions and 29 deletions

View File

@ -22,7 +22,6 @@ import io.netty.util.DefaultAttributeMap;
import io.netty.util.Recycler;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.internal.OneTimeTask;
import io.netty.util.internal.RecyclableMpscLinkedQueueNode;
@ -63,7 +62,7 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap impleme
private volatile Runnable invokeChannelWritableStateChangedTask;
private volatile Runnable invokeFlushTask;
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutorGroup group, String name,
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name,
boolean inbound, boolean outbound) {
if (name == null) {
@ -72,20 +71,7 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap impleme
this.pipeline = pipeline;
this.name = name;
if (group != null) {
// Pin one of the child executors once and remember it so that the same child executor
// is used to fire events for the same channel.
EventExecutor childExecutor = pipeline.childExecutors.get(group);
if (childExecutor == null) {
childExecutor = group.next();
pipeline.childExecutors.put(group, childExecutor);
}
executor = childExecutor;
} else {
executor = null;
}
this.executor = executor;
this.inbound = inbound;
this.outbound = outbound;
}

View File

@ -15,15 +15,15 @@
*/
package io.netty.channel;
import io.netty.util.concurrent.EventExecutorGroup;
import io.netty.util.concurrent.EventExecutor;
final class DefaultChannelHandlerContext extends AbstractChannelHandlerContext {
private final ChannelHandler handler;
DefaultChannelHandlerContext(
DefaultChannelPipeline pipeline, EventExecutorGroup group, String name, ChannelHandler handler) {
super(pipeline, group, name, isInbound(handler), isOutbound(handler));
DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
super(pipeline, executor, name, isInbound(handler), isOutbound(handler));
if (handler == null) {
throw new NullPointerException("handler");
}

View File

@ -59,8 +59,7 @@ final class DefaultChannelPipeline implements ChannelPipeline {
final AbstractChannelHandlerContext head;
final AbstractChannelHandlerContext tail;
final Map<EventExecutorGroup, EventExecutor> childExecutors =
new IdentityHashMap<EventExecutorGroup, EventExecutor>();
private Map<EventExecutorGroup, EventExecutor> childExecutors;
public DefaultChannelPipeline(AbstractChannel channel) {
if (channel == null) {
@ -75,6 +74,29 @@ final class DefaultChannelPipeline implements ChannelPipeline {
tail.prev = head;
}
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler);
}
private EventExecutor childExecutor(EventExecutorGroup group) {
if (group == null) {
return null;
}
Map<EventExecutorGroup, EventExecutor> childExecutors = this.childExecutors;
if (childExecutors == null) {
// Use size of 4 as most people only use one extra EventExecutor.
childExecutors = this.childExecutors = new IdentityHashMap<EventExecutorGroup, EventExecutor>(4);
}
// Pin one of the child executors once and remember it so that the same child executor
// is used to fire events for the same channel.
EventExecutor childExecutor = childExecutors.get(group);
if (childExecutor == null) {
childExecutor = group.next();
childExecutors.put(group, childExecutor);
}
return childExecutor;
}
@Override
public Channel channel() {
return channel;
@ -89,7 +111,7 @@ final class DefaultChannelPipeline implements ChannelPipeline {
public ChannelPipeline addFirst(EventExecutorGroup group, final String name, ChannelHandler handler) {
synchronized (this) {
checkDuplicateName(name);
AbstractChannelHandlerContext newCtx = new DefaultChannelHandlerContext(this, group, name, handler);
AbstractChannelHandlerContext newCtx = newContext(group, name, handler);
addFirst0(newCtx);
}
@ -117,8 +139,7 @@ final class DefaultChannelPipeline implements ChannelPipeline {
public ChannelPipeline addLast(EventExecutorGroup group, final String name, ChannelHandler handler) {
synchronized (this) {
checkDuplicateName(name);
AbstractChannelHandlerContext newCtx = new DefaultChannelHandlerContext(this, group, name, handler);
AbstractChannelHandlerContext newCtx = newContext(group, name, handler);
addLast0(newCtx);
}
@ -148,7 +169,7 @@ final class DefaultChannelPipeline implements ChannelPipeline {
synchronized (this) {
AbstractChannelHandlerContext ctx = getContextOrDie(baseName);
checkDuplicateName(name);
AbstractChannelHandlerContext newCtx = new DefaultChannelHandlerContext(this, group, name, handler);
AbstractChannelHandlerContext newCtx = newContext(group, name, handler);
addBefore0(ctx, newCtx);
}
return this;
@ -176,8 +197,7 @@ final class DefaultChannelPipeline implements ChannelPipeline {
synchronized (this) {
AbstractChannelHandlerContext ctx = getContextOrDie(baseName);
checkDuplicateName(name);
AbstractChannelHandlerContext newCtx = new DefaultChannelHandlerContext(this, group, name, handler);
AbstractChannelHandlerContext newCtx = newContext(group, name, handler);
addAfter0(ctx, newCtx);
}
@ -379,8 +399,7 @@ final class DefaultChannelPipeline implements ChannelPipeline {
checkDuplicateName(newName);
}
final AbstractChannelHandlerContext newCtx =
new DefaultChannelHandlerContext(this, ctx.executor, newName, newHandler);
final AbstractChannelHandlerContext newCtx = newContext(ctx.executor, newName, newHandler);
if (!newCtx.channel().isRegistered() || newCtx.executor().inEventLoop()) {
replace0(ctx, newCtx);