diff --git a/common/src/main/java/io/netty/util/Recycler.java b/common/src/main/java/io/netty/util/Recycler.java index c1b4dd1e7e..2937d5a33f 100644 --- a/common/src/main/java/io/netty/util/Recycler.java +++ b/common/src/main/java/io/netty/util/Recycler.java @@ -36,8 +36,17 @@ public abstract class Recycler { private static final InternalLogger logger = InternalLoggerFactory.getInstance(Recycler.class); + @SuppressWarnings("rawtypes") + private static final Handle NOOP_HANDLE = new Handle() { + @Override + public void recycle(Object object) { + // NOOP + } + }; private static final AtomicInteger ID_GENERATOR = new AtomicInteger(Integer.MIN_VALUE); private static final int OWN_THREAD_ID = ID_GENERATOR.getAndIncrement(); + // TODO: Some arbitrary large number - should adjust as we get more production experience. + private static final int DEFAULT_INITIAL_MAX_CAPACITY = 262144; private static final int DEFAULT_MAX_CAPACITY; private static final int INITIAL_CAPACITY; @@ -45,7 +54,7 @@ public abstract class Recycler { // In the future, we might have different maxCapacity for different object types. // e.g. io.netty.recycler.maxCapacity.writeTask // io.netty.recycler.maxCapacity.outboundBuffer - int maxCapacity = SystemPropertyUtil.getInt("io.netty.recycler.maxCapacity", 0); + int maxCapacity = SystemPropertyUtil.getInt("io.netty.recycler.maxCapacity", DEFAULT_INITIAL_MAX_CAPACITY); if (maxCapacity <= 0) { // TODO: Some arbitrary large number - should adjust as we get more production experience. maxCapacity = 262144; @@ -53,7 +62,11 @@ public abstract class Recycler { DEFAULT_MAX_CAPACITY = maxCapacity; if (logger.isDebugEnabled()) { - logger.debug("-Dio.netty.recycler.maxCapacity: {}", DEFAULT_MAX_CAPACITY); + if (DEFAULT_MAX_CAPACITY == 0) { + logger.debug("-Dio.netty.recycler.maxCapacity.maxCapacity: disabled"); + } else { + logger.debug("-Dio.netty.recycler.maxCapacity.maxCapacity: {}", DEFAULT_MAX_CAPACITY); + } } INITIAL_CAPACITY = Math.min(DEFAULT_MAX_CAPACITY, 256); @@ -77,6 +90,9 @@ public abstract class Recycler { @SuppressWarnings("unchecked") public final T get() { + if (maxCapacity == 0) { + return newObject((Handle) NOOP_HANDLE); + } Stack stack = threadLocal.get(); DefaultHandle handle = stack.pop(); if (handle == null) { @@ -87,6 +103,10 @@ public abstract class Recycler { } public final boolean recycle(T o, Handle handle) { + if (handle == NOOP_HANDLE) { + return false; + } + DefaultHandle h = (DefaultHandle) handle; if (h.stack.parent != this) { return false; diff --git a/common/src/test/java/io/netty/util/RecyclerTest.java b/common/src/test/java/io/netty/util/RecyclerTest.java index f0c72269c9..f226b15020 100644 --- a/common/src/test/java/io/netty/util/RecyclerTest.java +++ b/common/src/test/java/io/netty/util/RecyclerTest.java @@ -40,6 +40,15 @@ public class RecyclerTest { object2.recycle(); } + @Test + public void testRecycleDisable() { + DisabledRecyclableObject object = DisabledRecyclableObject.newInstance(); + object.recycle(); + DisabledRecyclableObject object2 = DisabledRecyclableObject.newInstance(); + assertNotSame(object, object2); + object2.recycle(); + } + static final class RecyclableObject { private static final Recycler RECYCLER = new Recycler() { @@ -64,6 +73,30 @@ public class RecyclerTest { } } + static final class DisabledRecyclableObject { + + private static final Recycler RECYCLER = new Recycler(-1) { + @Override + protected DisabledRecyclableObject newObject(Handle handle) { + return new DisabledRecyclableObject(handle); + } + }; + + private final Recycler.Handle handle; + + private DisabledRecyclableObject(Recycler.Handle handle) { + this.handle = handle; + } + + public static DisabledRecyclableObject newInstance() { + return RECYCLER.get(); + } + + public void recycle() { + RECYCLER.recycle(this, handle); + } + } + /** * Test to make sure bug #2848 never happens again * https://github.com/netty/netty/issues/2848