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.
This commit is contained in:
Xiaoyan Lin 2016-01-30 20:14:47 -08:00 committed by Norman Maurer
parent b354868dd8
commit b7415a3307
12 changed files with 50 additions and 22 deletions

View File

@ -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<Cookie> cookiesList = new ArrayList<Cookie>();
List<Cookie> cookiesList = InternalThreadLocalMap.get().arrayList();
cookiesList.add(firstCookie);
while (cookiesIt.hasNext()) {
cookiesList.add(cookiesIt.next());

View File

@ -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<String> values = new ArrayList<String>(1);
List<String> values = InternalThreadLocalMap.get().arrayList(1);
boolean inQuote = false;
boolean escapeNext = false;
int start = 0;

View File

@ -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<CharSequence>
* Splits the specified {@link String} with the specified delimiter..
*/
public AsciiString[] split(char delim) {
final List<AsciiString> res = new ArrayList<AsciiString>();
final List<AsciiString> res = InternalThreadLocalMap.get().arrayList();
int start = 0;
final int length = length();

View File

@ -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 <E> ArrayList<E> arrayList() {
return arrayList(DEFAULT_ARRAY_LIST_INITIAL_CAPACITY);
}
public <E> ArrayList<E> arrayList(int minCapacity) {
ArrayList<E> list = (ArrayList<E>) arrayList;
if (list == null) {
list = (ArrayList<E>) new ArrayList<Object>(minCapacity);
} else {
list.clear();
list.ensureCapacity(minCapacity);
}
return list;
}
public int futureListenerStackDepth() {
return futureListenerStackDepth;
}

View File

@ -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<String> res = new ArrayList<String>();
final List<String> 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<String> res = new ArrayList<String>();
final List<String> res = InternalThreadLocalMap.get().arrayList();
int start = 0;
int cpt = 1;

View File

@ -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<Charset, CharsetEncoder> charsetEncoderCache;
Map<Charset, CharsetDecoder> charsetDecoderCache;
// ArrayList-related thread-locals
ArrayList<Object> arrayList;
UnpaddedInternalThreadLocalMap(Object[] indexedVariables) {
this.indexedVariables = indexedVariables;
}

View File

@ -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<String> newCiphers = new ArrayList<String>(supportedCiphers.size());
List<String> newCiphers = InternalThreadLocalMap.get().arrayList(supportedCiphers.size());
for (String c : ciphers) {
if (c == null) {
break;

View File

@ -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<String> enabled = new ArrayList<String>();
List<String> 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);

View File

@ -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<String> newCiphers;
if (ciphers == null) {
newCiphers = new ArrayList<String>(defaultCiphers.size());
newCiphers = InternalThreadLocalMap.get().arrayList(defaultCiphers.size());
ciphers = defaultCiphers;
} else {
newCiphers = new ArrayList<String>(supportedCiphers.size());
newCiphers = InternalThreadLocalMap.get().arrayList(supportedCiphers.size());
}
for (String c : ciphers) {
if (c == null) {

View File

@ -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<byte[]> list = new ArrayList<byte[]>();
List<byte[]> 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<byte[]> list = new ArrayList<byte[]>();
List<byte[]> list = InternalThreadLocalMap.get().arrayList();
for (String f: fingerprints) {
if (f == null) {
break;

View File

@ -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<InternetProtocolFamily> list =
new ArrayList<InternetProtocolFamily>(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<InternetProtocolFamily> list =
new ArrayList<InternetProtocolFamily>(InternetProtocolFamily.values().length);
InternalThreadLocalMap.get().arrayList(InternetProtocolFamily.values().length);
for (InternetProtocolFamily f : resolvedAddressTypes) {
if (f == null) {

View File

@ -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<InetSocketAddress> list = new ArrayList<InetSocketAddress>(addresses.length);
List<InetSocketAddress> list = InternalThreadLocalMap.get().arrayList(addresses.length);
for (InetSocketAddress a: addresses) {
if (a == null) {
break;