diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java index d5e6980dac..20f7506159 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java @@ -17,6 +17,7 @@ package io.netty.buffer; import io.netty.util.ThreadDeathWatcher; +import io.netty.util.internal.FastThreadLocal; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.logging.InternalLogger; @@ -28,7 +29,6 @@ import java.util.concurrent.atomic.AtomicInteger; public class PooledByteBufAllocator extends AbstractByteBufAllocator { private static final InternalLogger logger = InternalLoggerFactory.getInstance(PooledByteBufAllocator.class); - private static final int DEFAULT_NUM_HEAP_ARENA; private static final int DEFAULT_NUM_DIRECT_ARENA; @@ -273,7 +273,7 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator { threadCache.free(); } - final class PoolThreadLocalCache extends ThreadLocal { + final class PoolThreadLocalCache extends FastThreadLocal { private final AtomicInteger index = new AtomicInteger(); private boolean initialized; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java index 1fba709e38..57e2c261b4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java @@ -16,9 +16,11 @@ package io.netty.handler.codec.http; +import io.netty.util.internal.FastThreadLocal; + final class CookieEncoderUtil { - static final ThreadLocal buffer = new ThreadLocal() { + static final ThreadLocal buffer = new FastThreadLocal() { @Override public StringBuilder get() { StringBuilder buf = super.get(); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderDateFormat.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderDateFormat.java index b94abd8b62..c70de41da5 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderDateFormat.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaderDateFormat.java @@ -15,6 +15,8 @@ */ package io.netty.handler.codec.http; +import io.netty.util.internal.FastThreadLocal; + import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Date; @@ -38,7 +40,7 @@ final class HttpHeaderDateFormat extends SimpleDateFormat { private final SimpleDateFormat format2 = new HttpHeaderDateFormatObsolete2(); private static final ThreadLocal dateFormatThreadLocal = - new ThreadLocal() { + new FastThreadLocal() { @Override protected HttpHeaderDateFormat initialValue() { return new HttpHeaderDateFormat(); diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalMarshallerProvider.java b/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalMarshallerProvider.java index a3ca6f67b7..a64f5b8595 100644 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalMarshallerProvider.java +++ b/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalMarshallerProvider.java @@ -17,6 +17,7 @@ package io.netty.handler.codec.marshalling; import io.netty.channel.ChannelHandlerContext; +import io.netty.util.internal.FastThreadLocal; import org.jboss.marshalling.Marshaller; import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.MarshallingConfiguration; @@ -27,7 +28,7 @@ import org.jboss.marshalling.MarshallingConfiguration; * many small {@link Object}'s and your actual Thread count is not to big */ public class ThreadLocalMarshallerProvider implements MarshallerProvider { - private final ThreadLocal marshallers = new ThreadLocal(); + private final ThreadLocal marshallers = new FastThreadLocal(); private final MarshallerFactory factory; private final MarshallingConfiguration config; diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalUnmarshallerProvider.java b/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalUnmarshallerProvider.java index 77b278d624..2c31903ef1 100644 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalUnmarshallerProvider.java +++ b/codec/src/main/java/io/netty/handler/codec/marshalling/ThreadLocalUnmarshallerProvider.java @@ -17,6 +17,7 @@ package io.netty.handler.codec.marshalling; import io.netty.channel.ChannelHandlerContext; +import io.netty.util.internal.FastThreadLocal; import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.MarshallingConfiguration; import org.jboss.marshalling.Unmarshaller; @@ -27,7 +28,7 @@ import org.jboss.marshalling.Unmarshaller; * many small {@link Object}'s. */ public class ThreadLocalUnmarshallerProvider implements UnmarshallerProvider { - private final ThreadLocal unmarshallers = new ThreadLocal(); + private final ThreadLocal unmarshallers = new FastThreadLocal(); private final MarshallerFactory factory; private final MarshallingConfiguration config; diff --git a/common/src/main/java/io/netty/util/CharsetUtil.java b/common/src/main/java/io/netty/util/CharsetUtil.java index d11c07f6b1..d2c20df35d 100644 --- a/common/src/main/java/io/netty/util/CharsetUtil.java +++ b/common/src/main/java/io/netty/util/CharsetUtil.java @@ -15,6 +15,8 @@ */ package io.netty.util; +import io.netty.util.internal.FastThreadLocal; + import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; @@ -61,7 +63,7 @@ public final class CharsetUtil { public static final Charset US_ASCII = Charset.forName("US-ASCII"); private static final ThreadLocal> encoders = - new ThreadLocal>() { + new FastThreadLocal>() { @Override protected Map initialValue() { return new IdentityHashMap(); @@ -69,7 +71,7 @@ public final class CharsetUtil { }; private static final ThreadLocal> decoders = - new ThreadLocal>() { + new FastThreadLocal>() { @Override protected Map initialValue() { return new IdentityHashMap(); diff --git a/common/src/main/java/io/netty/util/Recycler.java b/common/src/main/java/io/netty/util/Recycler.java index ad115c42e1..3d96036154 100644 --- a/common/src/main/java/io/netty/util/Recycler.java +++ b/common/src/main/java/io/netty/util/Recycler.java @@ -23,6 +23,8 @@ import io.netty.util.internal.logging.InternalLoggerFactory; import java.util.IdentityHashMap; import java.util.Map; +import io.netty.util.internal.FastThreadLocal; + /** * Light-weight object pool based on a thread-local stack. * @@ -54,8 +56,7 @@ public abstract class Recycler { } private final int maxCapacity; - - private final ThreadLocal> threadLocal = new ThreadLocal>() { + private final ThreadLocal> threadLocal = new FastThreadLocal>() { @Override protected Stack initialValue() { return new Stack(Recycler.this, Thread.currentThread(), maxCapacity); @@ -66,11 +67,8 @@ public abstract class Recycler { this(DEFAULT_MAX_CAPACITY); } - protected Recycler(int maxCapacity) { - if (maxCapacity <= 0) { - maxCapacity = 0; - } - this.maxCapacity = maxCapacity; + public Recycler(int maxCapacity) { + this.maxCapacity = Math.max(0, maxCapacity); } public final T get() { diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java index c478627b32..c975e419d6 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java +++ b/common/src/main/java/io/netty/util/concurrent/DefaultPromise.java @@ -17,6 +17,7 @@ package io.netty.util.concurrent; import io.netty.util.Signal; import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.FastThreadLocal; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; import io.netty.util.internal.logging.InternalLogger; @@ -35,7 +36,7 @@ public class DefaultPromise extends AbstractFuture implements Promise { InternalLoggerFactory.getInstance(DefaultPromise.class.getName() + ".rejectedExecution"); private static final int MAX_LISTENER_STACK_DEPTH = 8; - private static final ThreadLocal LISTENER_STACK_DEPTH = new ThreadLocal() { + private static final ThreadLocal LISTENER_STACK_DEPTH = new FastThreadLocal() { @Override protected Integer initialValue() { return 0; diff --git a/common/src/main/java/io/netty/util/concurrent/DefaultThreadFactory.java b/common/src/main/java/io/netty/util/concurrent/DefaultThreadFactory.java index 3142a1b029..94a89654b8 100644 --- a/common/src/main/java/io/netty/util/concurrent/DefaultThreadFactory.java +++ b/common/src/main/java/io/netty/util/concurrent/DefaultThreadFactory.java @@ -16,6 +16,7 @@ package io.netty.util.concurrent; +import io.netty.util.internal.FastThreadLocalThread; import io.netty.util.internal.StringUtil; import java.util.Locale; @@ -98,7 +99,7 @@ public class DefaultThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { - Thread t = new Thread(r, prefix + nextId.incrementAndGet()); + Thread t = newThread(r, prefix + nextId.incrementAndGet()); try { if (t.isDaemon()) { if (!daemon) { @@ -118,4 +119,8 @@ public class DefaultThreadFactory implements ThreadFactory { } return t; } + + protected Thread newThread(Runnable r, String name) { + return new FastThreadLocalThread(r, name); + } } diff --git a/common/src/main/java/io/netty/util/internal/FastThreadLocal.java b/common/src/main/java/io/netty/util/internal/FastThreadLocal.java new file mode 100644 index 0000000000..7e0aec53a4 --- /dev/null +++ b/common/src/main/java/io/netty/util/internal/FastThreadLocal.java @@ -0,0 +1,112 @@ +/* + * Copyright 2014 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.util.internal; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A special {@link ThreadLocal} which is operating over a predefined array, so it always operate in O(1) when called + * from a {@link FastThreadLocalThread}. This permits less indirection and offers a slight performance improvement, + * so is useful when invoked frequently. + * + * The fast path is only possible on threads that extend FastThreadLocalThread, as this class + * stores the necessary state. Access by any other kind of thread falls back to a regular ThreadLocal + * + * @param + */ +public class FastThreadLocal extends ThreadLocal { + static final Object EMPTY = new Object(); + + private static final AtomicInteger NEXT_INDEX = new AtomicInteger(0); + private final ThreadLocal fallback = new ThreadLocal() { + @Override + protected V initialValue() { + return FastThreadLocal.this.initialValue(); + } + }; + private final int index; + + public FastThreadLocal() { + index = NEXT_INDEX.getAndIncrement(); + if (index < 0) { + NEXT_INDEX.decrementAndGet(); + throw new IllegalStateException("Maximal number (" + Integer.MAX_VALUE + ") of FastThreadLocal exceeded"); + } + } + + /** + * Set the value for the current thread + */ + @Override + public void set(V value) { + Thread thread = Thread.currentThread(); + if (!(thread instanceof FastThreadLocalThread)) { + fallback.set(value); + return; + } + FastThreadLocalThread fastThread = (FastThreadLocalThread) thread; + Object[] lookup = fastThread.lookup; + if (index >= lookup.length) { + lookup = fastThread.expandArray(index); + } + lookup[index] = value; + } + + /** + * Sets the value to uninitialized; a proceeding call to get() will trigger a call to initialValue() + */ + @Override + public void remove() { + Thread thread = Thread.currentThread(); + if (!(thread instanceof FastThreadLocalThread)) { + fallback.remove(); + return; + } + Object[] lookup = ((FastThreadLocalThread) thread).lookup; + if (index >= lookup.length) { + return; + } + lookup[index] = EMPTY; + } + + /** + * @return the current value for the current thread + */ + @Override + @SuppressWarnings("unchecked") + public V get() { + Thread thread = Thread.currentThread(); + if (!(thread instanceof FastThreadLocalThread)) { + return fallback.get(); + } + FastThreadLocalThread fastThread = (FastThreadLocalThread) thread; + + Object[] lookup = fastThread.lookup; + Object v; + if (index >= lookup.length) { + v = initialValue(); + lookup = fastThread.expandArray(index); + lookup[index] = v; + } else { + v = lookup[index]; + if (v == EMPTY) { + v = initialValue(); + lookup[index] = v; + } + } + return (V) v; + } +} diff --git a/common/src/main/java/io/netty/util/internal/FastThreadLocalThread.java b/common/src/main/java/io/netty/util/internal/FastThreadLocalThread.java new file mode 100644 index 0000000000..dd7d2acd8a --- /dev/null +++ b/common/src/main/java/io/netty/util/internal/FastThreadLocalThread.java @@ -0,0 +1,82 @@ +/* +* Copyright 2014 The Netty Project +* +* The Netty Project licenses this file to you under the Apache License, +* version 2.0 (the "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at: +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +* License for the specific language governing permissions and limitations +* under the License. +*/ +package io.netty.util.internal; + +import java.util.Arrays; + +/** + * To utilise the {@link FastThreadLocal} fast-path, all threads accessing a {@link FastThreadLocal} must extend this + * class. + */ +public class FastThreadLocalThread extends Thread { + + Object[] lookup = newArray(); + + public FastThreadLocalThread() { } + + public FastThreadLocalThread(Runnable target) { + super(target); + } + + public FastThreadLocalThread(ThreadGroup group, Runnable target) { + super(group, target); + } + + public FastThreadLocalThread(String name) { + super(name); + } + + public FastThreadLocalThread(ThreadGroup group, String name) { + super(group, name); + } + + public FastThreadLocalThread(Runnable target, String name) { + super(target, name); + } + + public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) { + super(group, target, name); + } + + public FastThreadLocalThread(ThreadGroup group, Runnable target, String name, long stackSize) { + super(group, target, name, stackSize); + } + + private static Object[] newArray() { + Object[] array = new Object[32]; + Arrays.fill(array, FastThreadLocal.EMPTY); + return array; + } + + Object[] expandArray(int length) { + int newCapacity = lookup.length; + do { + // double capacity until it is big enough + newCapacity <<= 1; + + if (newCapacity < 0) { + throw new IllegalStateException(); + } + + } while (length > newCapacity); + + Object[] array = new Object[newCapacity]; + System.arraycopy(lookup, 0, array, 0, lookup.length); + Arrays.fill(array, lookup.length, array.length, FastThreadLocal.EMPTY); + lookup = array; + return lookup; + } +} diff --git a/common/src/main/java/io/netty/util/internal/TypeParameterMatcher.java b/common/src/main/java/io/netty/util/internal/TypeParameterMatcher.java index ab9a676b45..d507f5c5ba 100644 --- a/common/src/main/java/io/netty/util/internal/TypeParameterMatcher.java +++ b/common/src/main/java/io/netty/util/internal/TypeParameterMatcher.java @@ -31,7 +31,7 @@ public abstract class TypeParameterMatcher { private static final Object TEST_OBJECT = new Object(); private static final ThreadLocal, TypeParameterMatcher>> getCache = - new ThreadLocal, TypeParameterMatcher>>() { + new FastThreadLocal, TypeParameterMatcher>>() { @Override protected Map, TypeParameterMatcher> initialValue() { return new IdentityHashMap, TypeParameterMatcher>(); @@ -69,7 +69,7 @@ public abstract class TypeParameterMatcher { } private static final ThreadLocal, Map>> findCache = - new ThreadLocal, Map>>() { + new FastThreadLocal, Map>>() { @Override protected Map, Map> initialValue() { return new IdentityHashMap, Map>(); 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 218ae1a48b..bf1d3cde49 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 @@ -19,6 +19,7 @@ package io.netty.handler.ssl.util; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.util.internal.EmptyArrays; +import io.netty.util.internal.FastThreadLocal; import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.TrustManager; @@ -67,7 +68,7 @@ public final class FingerprintTrustManagerFactory extends SimpleTrustManagerFact private static final int SHA1_BYTE_LEN = 20; private static final int SHA1_HEX_LEN = SHA1_BYTE_LEN * 2; - private static final ThreadLocal tlmd = new ThreadLocal() { + private static final ThreadLocal tlmd = new FastThreadLocal() { @Override protected MessageDigest initialValue() { try { diff --git a/handler/src/main/java/io/netty/handler/ssl/util/SimpleTrustManagerFactory.java b/handler/src/main/java/io/netty/handler/ssl/util/SimpleTrustManagerFactory.java index b05e506ec9..7ac372a12f 100644 --- a/handler/src/main/java/io/netty/handler/ssl/util/SimpleTrustManagerFactory.java +++ b/handler/src/main/java/io/netty/handler/ssl/util/SimpleTrustManagerFactory.java @@ -16,6 +16,8 @@ package io.netty.handler.ssl.util; +import io.netty.util.internal.FastThreadLocal; + import javax.net.ssl.ManagerFactoryParameters; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; @@ -43,7 +45,7 @@ public abstract class SimpleTrustManagerFactory extends TrustManagerFactory { * To work around this issue, we use an ugly hack which uses a {@link ThreadLocal}. */ private static final ThreadLocal CURRENT_SPI = - new ThreadLocal() { + new FastThreadLocal() { @Override protected SimpleTrustManagerFactorySpi initialValue() { return new SimpleTrustManagerFactorySpi(); diff --git a/microbench/src/test/java/io/netty/microbench/buffer/ByteBufAllocatorBenchmark.java b/microbench/src/test/java/io/netty/microbench/buffer/ByteBufAllocatorBenchmark.java index aa361aff13..9158855f85 100644 --- a/microbench/src/test/java/io/netty/microbench/buffer/ByteBufAllocatorBenchmark.java +++ b/microbench/src/test/java/io/netty/microbench/buffer/ByteBufAllocatorBenchmark.java @@ -83,4 +83,16 @@ public class ByteBufAllocatorBenchmark extends AbstractMicrobenchmark { } pooledDirectBuffers[idx] = pooledAllocator.directBuffer(size); } + + @GenerateMicroBenchmark + public void defaultPooledHeapAllocAndFree() { + ByteBuf buffer = PooledByteBufAllocator.DEFAULT.heapBuffer(size); + buffer.release(); + } + + @GenerateMicroBenchmark + public void defaultPooledDirectAllocAndFree() { + ByteBuf buffer = PooledByteBufAllocator.DEFAULT.directBuffer(size); + buffer.release(); + } } diff --git a/microbench/src/test/java/io/netty/microbench/internal/RecyclableArrayListBenchmark.java b/microbench/src/test/java/io/netty/microbench/internal/RecyclableArrayListBenchmark.java new file mode 100644 index 0000000000..e10599fed5 --- /dev/null +++ b/microbench/src/test/java/io/netty/microbench/internal/RecyclableArrayListBenchmark.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.microbench.internal; + +import io.netty.microbench.util.AbstractMicrobenchmark; +import io.netty.util.internal.RecyclableArrayList; +import org.openjdk.jmh.annotations.GenerateMicroBenchmark; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Threads; + +/** + * This class benchmarks different allocators with different allocation sizes. + */ +@Threads(4) +@Measurement(iterations = 10, batchSize = 100) +public class RecyclableArrayListBenchmark extends AbstractMicrobenchmark { + + @Param({ "00000", "00256", "01024", "04096", "16384", "65536" }) + public int size; + + @GenerateMicroBenchmark + public void recycleSameThread() { + RecyclableArrayList list = RecyclableArrayList.newInstance(size); + list.recycle(); + } +} diff --git a/microbench/src/test/java/io/netty/microbench/util/AbstractMicrobenchmark.java b/microbench/src/test/java/io/netty/microbench/util/AbstractMicrobenchmark.java index 82adf78133..d9ab838e7c 100644 --- a/microbench/src/test/java/io/netty/microbench/util/AbstractMicrobenchmark.java +++ b/microbench/src/test/java/io/netty/microbench/util/AbstractMicrobenchmark.java @@ -16,6 +16,7 @@ package io.netty.microbench.util; import io.netty.util.ResourceLeakDetector; +import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.internal.SystemPropertyUtil; import org.junit.Test; import org.openjdk.jmh.annotations.Fork; @@ -29,6 +30,9 @@ import org.openjdk.jmh.runner.options.ChainedOptionsBuilder; import org.openjdk.jmh.runner.options.OptionsBuilder; import java.io.File; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; /** * Base class for all JMH benchmarks. @@ -43,11 +47,21 @@ public class AbstractMicrobenchmark { protected static final int DEFAULT_MEASURE_ITERATIONS = 10; protected static final int DEFAULT_FORKS = 2; + public static final class HarnessExecutor extends ThreadPoolExecutor { + public HarnessExecutor(int maxThreads, String prefix) { + super(0, maxThreads, 1L, TimeUnit.DAYS, new SynchronousQueue(), + new DefaultThreadFactory(prefix)); + System.out.println("Using harness executor"); + } + } + protected static final String[] JVM_ARGS = { "-server", "-dsa", "-da", "-ea:io.netty...", "-Xms768m", "-Xmx768m", "-XX:MaxDirectMemorySize=768m", "-XX:+AggressiveOpts", "-XX:+UseBiasedLocking", "-XX:+UseFastAccessorMethods", "-XX:+UseStringCache", "-XX:+OptimizeStringConcat", - "-XX:+HeapDumpOnOutOfMemoryError", "-Dio.netty.noResourceLeakDetection" + "-XX:+HeapDumpOnOutOfMemoryError", "-Dio.netty.noResourceLeakDetection", + "-Dharness.executor=CUSTOM", + "-Dharness.executor.class=io.netty.microbench.util.AbstractMicrobenchmark$HarnessExecutor" }; static { diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java index 1f9496562e..b1518451e4 100644 --- a/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java +++ b/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java @@ -16,6 +16,8 @@ package io.netty.channel; +import io.netty.util.internal.FastThreadLocal; + import java.util.Map; import java.util.WeakHashMap; @@ -33,7 +35,7 @@ public abstract class ChannelHandlerAdapter implements ChannelHandler { * See #2289. */ private static final ThreadLocal, Boolean>> SHARABLE_CACHE = - new ThreadLocal, Boolean>>() { + new FastThreadLocal, Boolean>>() { @Override protected Map, Boolean> initialValue() { // Start with small capacity to keep memory overhead as low as possible. diff --git a/transport/src/main/java/io/netty/channel/local/LocalChannel.java b/transport/src/main/java/io/netty/channel/local/LocalChannel.java index ccf44bd9b4..12305a8b47 100644 --- a/transport/src/main/java/io/netty/channel/local/LocalChannel.java +++ b/transport/src/main/java/io/netty/channel/local/LocalChannel.java @@ -28,6 +28,7 @@ import io.netty.channel.EventLoop; import io.netty.channel.SingleThreadEventLoop; import io.netty.util.ReferenceCountUtil; import io.netty.util.concurrent.SingleThreadEventExecutor; +import io.netty.util.internal.FastThreadLocal; import java.net.SocketAddress; import java.nio.channels.AlreadyConnectedException; @@ -48,7 +49,7 @@ public class LocalChannel extends AbstractChannel { private static final ChannelMetadata METADATA = new ChannelMetadata(false); private static final int MAX_READER_STACK_DEPTH = 8; - private static final ThreadLocal READER_STACK_DEPTH = new ThreadLocal() { + private static final ThreadLocal READER_STACK_DEPTH = new FastThreadLocal() { @Override protected Integer initialValue() { return 0;