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:
parent
19950f89e4
commit
01da38d21a
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user