From 148abe52f92ef59d21999e7706d0df4dc5dfd685 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Thu, 7 Mar 2013 12:43:16 +0900 Subject: [PATCH] Reduce the amount of memory used for handler names - Fixes #1123 --- .../netty/channel/DefaultChannelPipeline.java | 64 +++++++++++++++---- 1 file changed, 51 insertions(+), 13 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java index 970c842743..7344172040 100755 --- a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java @@ -33,6 +33,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.WeakHashMap; import java.util.concurrent.Future; import static io.netty.channel.DefaultChannelHandlerContext.*; @@ -45,6 +46,17 @@ final class DefaultChannelPipeline implements ChannelPipeline { static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultChannelPipeline.class); + @SuppressWarnings("unchecked") + private static final WeakHashMap, String>[] nameCaches = + new WeakHashMap[Runtime.getRuntime().availableProcessors()]; + + static { + for (int i = 0; i < nameCaches.length; i ++) { + nameCaches[i] = new WeakHashMap, String>(); + } + } + + final Channel channel; final DefaultChannelHandlerContext head; @@ -350,14 +362,40 @@ final class DefaultChannelPipeline implements ChannelPipeline { return this; } - private static String generateName(ChannelHandler handler) { - String type = handler.getClass().getSimpleName(); - StringBuilder buf = new StringBuilder(type.length() + 10); - buf.append(type); - buf.append("-0"); - buf.append(Long.toHexString(System.identityHashCode(handler) & 0xFFFFFFFFL | 0x100000000L)); - buf.setCharAt(buf.length() - 9, 'x'); - return buf.toString(); + private String generateName(ChannelHandler handler) { + WeakHashMap, String> cache = nameCaches[(int) (Thread.currentThread().getId() % nameCaches.length)]; + Class handlerType = handler.getClass(); + String name; + synchronized (cache) { + name = cache.get(handlerType); + if (name == null) { + Package pkg = handlerType.getPackage(); + if (pkg != null) { + name = handlerType.getName().substring(pkg.getName().length() + 1); + } else { + name = handlerType.getName(); + } + name += "#0"; + cache.put(handlerType, name); + } + } + + synchronized (this) { + // It's not very likely for a user to put more than one handler of the same type, but make sure to avoid + // any name conflicts. Note that we don't cache the names generated here. + if (name2ctx.containsKey(name)) { + String baseName = name.substring(0, name.length() - 1); // Strip the trailing '0'. + for (int i = 1;; i ++) { + String newName = baseName + i; + if (!name2ctx.containsKey(newName)) { + name = newName; + break; + } + } + } + } + + return name; } @Override @@ -792,6 +830,11 @@ final class DefaultChannelPipeline implements ChannelPipeline { } } + @Override + public Iterator> iterator() { + return toMap().entrySet().iterator(); + } + /** * Returns the {@link String} representation of this pipeline. */ @@ -1312,9 +1355,4 @@ final class DefaultChannelPipeline implements ChannelPipeline { unsafe.flush(promise); } } - - @Override - public Iterator> iterator() { - return toMap().entrySet().iterator(); - } }