From b7415a3307d9a36f2a67339c7596e0ee2b2a5644 Mon Sep 17 00:00:00 2001 From: Xiaoyan Lin Date: Sat, 30 Jan 2016 20:14:47 -0800 Subject: [PATCH] Add a reusable ArrayList to InternalThreadLocalMap Motivation: See #3411. A reusable ArrayList in InternalThreadLocalMap can avoid allocations in the following pattern: ``` List<...> list = new ArrayList<...>(); add something to list but never use InternalThreadLocalMap return list.toArray(new ...[list.size()]); ``` Modifications: Add a reusable ArrayList to InternalThreadLocalMap and update codes to use it. Result: Reuse a thread local ArrayList to avoid allocations. --- .../http/cookie/ClientCookieEncoder.java | 4 ++-- .../HttpPostMultipartRequestDecoder.java | 3 ++- .../main/java/io/netty/util/AsciiString.java | 4 ++-- .../util/internal/InternalThreadLocalMap.java | 21 +++++++++++++++++++ .../io/netty/util/internal/StringUtil.java | 5 ++--- .../UnpaddedInternalThreadLocalMap.java | 4 ++++ .../ssl/IdentityCipherSuiteFilter.java | 5 +++-- .../io/netty/handler/ssl/OpenSslEngine.java | 4 ++-- .../ssl/SupportedCipherSuiteFilter.java | 7 ++++--- .../util/FingerprintTrustManagerFactory.java | 6 +++--- .../resolver/dns/DnsNameResolverBuilder.java | 6 +++--- .../resolver/dns/DnsServerAddresses.java | 3 ++- 12 files changed, 50 insertions(+), 22 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java index ed902d39c4..0bf4e55205 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/cookie/ClientCookieEncoder.java @@ -22,8 +22,8 @@ import static io.netty.handler.codec.http.cookie.CookieUtil.stripTrailingSeparat import static io.netty.handler.codec.http.cookie.CookieUtil.stripTrailingSeparatorOrNull; import static io.netty.util.internal.ObjectUtil.checkNotNull; import io.netty.handler.codec.http.HttpRequest; +import io.netty.util.internal.InternalThreadLocalMap; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; @@ -196,7 +196,7 @@ public final class ClientCookieEncoder extends CookieEncoder { if (!cookiesIt.hasNext()) { encode(buf, firstCookie); } else { - List cookiesList = new ArrayList(); + List cookiesList = InternalThreadLocalMap.get().arrayList(); cookiesList.add(firstCookie); while (cookiesIt.hasNext()) { cookiesList.add(cookiesIt.next()); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java index fc58adc174..88030582c2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostMultipartRequestDecoder.java @@ -30,6 +30,7 @@ import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDec import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.MultiPartStatus; import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.NotEnoughDataDecoderException; import io.netty.util.CharsetUtil; +import io.netty.util.internal.InternalThreadLocalMap; import io.netty.util.internal.StringUtil; import java.io.IOException; @@ -1836,7 +1837,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest * @return an array of String where values that were separated by ';' or ',' */ private static String[] splitMultipartHeaderValues(String svalue) { - List values = new ArrayList(1); + List values = InternalThreadLocalMap.get().arrayList(1); boolean inQuote = false; boolean escapeNext = false; int start = 0; diff --git a/common/src/main/java/io/netty/util/AsciiString.java b/common/src/main/java/io/netty/util/AsciiString.java index dc799664e7..31adacd62f 100644 --- a/common/src/main/java/io/netty/util/AsciiString.java +++ b/common/src/main/java/io/netty/util/AsciiString.java @@ -17,13 +17,13 @@ package io.netty.util; import io.netty.util.ByteProcessor.IndexOfProcessor; import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.InternalThreadLocalMap; import io.netty.util.internal.PlatformDependent; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -1050,7 +1050,7 @@ public final class AsciiString implements CharSequence, Comparable * Splits the specified {@link String} with the specified delimiter.. */ public AsciiString[] split(char delim) { - final List res = new ArrayList(); + final List res = InternalThreadLocalMap.get().arrayList(); int start = 0; final int length = length(); diff --git a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java b/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java index 64f3c77ff1..dbd4b3d6bd 100644 --- a/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java +++ b/common/src/main/java/io/netty/util/internal/InternalThreadLocalMap.java @@ -22,6 +22,7 @@ import io.netty.util.concurrent.FastThreadLocalThread; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; +import java.util.ArrayList; import java.util.Arrays; import java.util.IdentityHashMap; import java.util.Map; @@ -34,6 +35,8 @@ import java.util.WeakHashMap; */ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap { + private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8; + public static final Object UNSET = new Object(); public static InternalThreadLocalMap getIfSet() { @@ -160,6 +163,9 @@ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap if (charsetDecoderCache != null) { count ++; } + if (arrayList != null) { + count ++; + } for (Object o: indexedVariables) { if (o != UNSET) { @@ -198,6 +204,21 @@ public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap return cache; } + public ArrayList arrayList() { + return arrayList(DEFAULT_ARRAY_LIST_INITIAL_CAPACITY); + } + + public ArrayList arrayList(int minCapacity) { + ArrayList list = (ArrayList) arrayList; + if (list == null) { + list = (ArrayList) new ArrayList(minCapacity); + } else { + list.clear(); + list.ensureCapacity(minCapacity); + } + return list; + } + public int futureListenerStackDepth() { return futureListenerStackDepth; } diff --git a/common/src/main/java/io/netty/util/internal/StringUtil.java b/common/src/main/java/io/netty/util/internal/StringUtil.java index 27349c3b31..473ba597d1 100644 --- a/common/src/main/java/io/netty/util/internal/StringUtil.java +++ b/common/src/main/java/io/netty/util/internal/StringUtil.java @@ -16,7 +16,6 @@ package io.netty.util.internal; import java.io.IOException; -import java.util.ArrayList; import java.util.Formatter; import java.util.List; @@ -94,7 +93,7 @@ public final class StringUtil { */ public static String[] split(String value, char delim) { final int end = value.length(); - final List res = new ArrayList(); + final List res = InternalThreadLocalMap.get().arrayList(); int start = 0; for (int i = 0; i < end; i ++) { @@ -136,7 +135,7 @@ public final class StringUtil { */ public static String[] split(String value, char delim, int maxParts) { final int end = value.length(); - final List res = new ArrayList(); + final List res = InternalThreadLocalMap.get().arrayList(); int start = 0; int cpt = 1; diff --git a/common/src/main/java/io/netty/util/internal/UnpaddedInternalThreadLocalMap.java b/common/src/main/java/io/netty/util/internal/UnpaddedInternalThreadLocalMap.java index 21caec99ef..c75ac40ab5 100644 --- a/common/src/main/java/io/netty/util/internal/UnpaddedInternalThreadLocalMap.java +++ b/common/src/main/java/io/netty/util/internal/UnpaddedInternalThreadLocalMap.java @@ -21,6 +21,7 @@ import io.netty.util.concurrent.FastThreadLocal; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; +import java.util.ArrayList; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; @@ -51,6 +52,9 @@ class UnpaddedInternalThreadLocalMap { Map charsetEncoderCache; Map charsetDecoderCache; + // ArrayList-related thread-locals + ArrayList arrayList; + UnpaddedInternalThreadLocalMap(Object[] indexedVariables) { this.indexedVariables = indexedVariables; } diff --git a/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java b/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java index c9ebfb0da2..fc6235c2aa 100644 --- a/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java +++ b/handler/src/main/java/io/netty/handler/ssl/IdentityCipherSuiteFilter.java @@ -15,7 +15,8 @@ */ package io.netty.handler.ssl; -import java.util.ArrayList; +import io.netty.util.internal.InternalThreadLocalMap; + import java.util.List; import java.util.Set; @@ -33,7 +34,7 @@ public final class IdentityCipherSuiteFilter implements CipherSuiteFilter { if (ciphers == null) { return defaultCiphers.toArray(new String[defaultCiphers.size()]); } else { - List newCiphers = new ArrayList(supportedCiphers.size()); + List newCiphers = InternalThreadLocalMap.get().arrayList(supportedCiphers.size()); for (String c : ciphers) { if (c == null) { break; diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java index 22bb1f6a6c..d70c952544 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.InternalThreadLocalMap; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import io.netty.util.internal.logging.InternalLogger; @@ -42,7 +43,6 @@ import java.nio.ByteBuffer; import java.nio.ReadOnlyBufferException; import java.security.Principal; import java.security.cert.Certificate; -import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -1018,7 +1018,7 @@ public final class OpenSslEngine extends SSLEngine { @Override public String[] getEnabledProtocols() { - List enabled = new ArrayList(); + List enabled = InternalThreadLocalMap.get().arrayList(); // Seems like there is no way to explict disable SSLv2Hello in openssl so it is always enabled enabled.add(PROTOCOL_SSL_V2_HELLO); diff --git a/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java b/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java index 132f1a0e5e..a90f3c8ffc 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java +++ b/handler/src/main/java/io/netty/handler/ssl/SupportedCipherSuiteFilter.java @@ -15,8 +15,9 @@ */ package io.netty.handler.ssl; +import io.netty.util.internal.InternalThreadLocalMap; + import javax.net.ssl.SSLEngine; -import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -40,10 +41,10 @@ public final class SupportedCipherSuiteFilter implements CipherSuiteFilter { final List newCiphers; if (ciphers == null) { - newCiphers = new ArrayList(defaultCiphers.size()); + newCiphers = InternalThreadLocalMap.get().arrayList(defaultCiphers.size()); ciphers = defaultCiphers; } else { - newCiphers = new ArrayList(supportedCiphers.size()); + newCiphers = InternalThreadLocalMap.get().arrayList(supportedCiphers.size()); } for (String c : ciphers) { if (c == null) { diff --git a/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java index cda1419a97..c4563a99dd 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/FingerprintTrustManagerFactory.java @@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.util.internal.EmptyArrays; import io.netty.util.concurrent.FastThreadLocal; +import io.netty.util.internal.InternalThreadLocalMap; import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.TrustManager; @@ -31,7 +32,6 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; @@ -151,7 +151,7 @@ public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFact throw new NullPointerException("fingerprints"); } - List list = new ArrayList(); + List list = InternalThreadLocalMap.get().arrayList(); for (byte[] f: fingerprints) { if (f == null) { break; @@ -171,7 +171,7 @@ public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFact throw new NullPointerException("fingerprints"); } - List list = new ArrayList(); + List list = InternalThreadLocalMap.get().arrayList(); for (String f: fingerprints) { if (f == null) { break; diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java index 8e05f6774a..2892d2e2ed 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsNameResolverBuilder.java @@ -23,9 +23,9 @@ import io.netty.channel.ReflectiveChannelFactory; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.InternetProtocolFamily; import io.netty.resolver.HostsFileEntriesResolver; +import io.netty.util.internal.InternalThreadLocalMap; import java.net.InetSocketAddress; -import java.util.ArrayList; import java.util.List; import static io.netty.util.internal.ObjectUtil.checkNotNull; @@ -170,7 +170,7 @@ public final class DnsNameResolverBuilder { checkNotNull(resolvedAddressTypes, "resolvedAddressTypes"); final List list = - new ArrayList(InternetProtocolFamily.values().length); + InternalThreadLocalMap.get().arrayList(InternetProtocolFamily.values().length); for (InternetProtocolFamily f : resolvedAddressTypes) { if (f == null) { @@ -207,7 +207,7 @@ public final class DnsNameResolverBuilder { checkNotNull(resolvedAddressTypes, "resolveAddressTypes"); final List list = - new ArrayList(InternetProtocolFamily.values().length); + InternalThreadLocalMap.get().arrayList(InternetProtocolFamily.values().length); for (InternetProtocolFamily f : resolvedAddressTypes) { if (f == null) { diff --git a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddresses.java b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddresses.java index 4561b64251..6d9c4b55d7 100644 --- a/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddresses.java +++ b/resolver-dns/src/main/java/io/netty/resolver/dns/DnsServerAddresses.java @@ -16,6 +16,7 @@ package io.netty.resolver.dns; +import io.netty.util.internal.InternalThreadLocalMap; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -241,7 +242,7 @@ public abstract class DnsServerAddresses { throw new NullPointerException("addresses"); } - List list = new ArrayList(addresses.length); + List list = InternalThreadLocalMap.get().arrayList(addresses.length); for (InetSocketAddress a: addresses) { if (a == null) { break;