diff --git a/common/src/main/java/io/netty/util/Recycler.java b/common/src/main/java/io/netty/util/Recycler.java index a56499cd7b..0600f66d70 100644 --- a/common/src/main/java/io/netty/util/Recycler.java +++ b/common/src/main/java/io/netty/util/Recycler.java @@ -96,6 +96,10 @@ public abstract class Recycler { return true; } + final int threadLocalCapacity() { + return threadLocal.get().elements.length; + } + protected abstract T newObject(Handle handle); public interface Handle { @@ -339,12 +343,12 @@ public abstract class Recycler { item.recycleId = item.lastRecycledId = OWN_THREAD_ID; int size = this.size; + if (size == maxCapacity) { + // Hit the maximum capacity - drop the possibly youngest object. + return; + } if (size == elements.length) { - if (size == maxCapacity) { - // Hit the maximum capacity - drop the possibly youngest object. - return; - } - elements = Arrays.copyOf(elements, size << 1); + elements = Arrays.copyOf(elements, Math.min(size << 1, maxCapacity)); } elements[size] = item; diff --git a/common/src/test/java/io/netty/util/RecyclerTest.java b/common/src/test/java/io/netty/util/RecyclerTest.java index 5212e9b4df..91114344b6 100644 --- a/common/src/test/java/io/netty/util/RecyclerTest.java +++ b/common/src/test/java/io/netty/util/RecyclerTest.java @@ -15,6 +15,8 @@ */ package io.netty.util; +import java.util.Random; + import org.junit.Assert; import org.junit.Test; @@ -59,4 +61,47 @@ public class RecyclerTest { RECYCLER.recycle(this, handle); } } + + /** + * Test to make sure bug #2848 never happens again + * https://github.com/netty/netty/issues/2848 + */ + @Test + public void testMaxCapacity() { + testMaxCapacity(300); + Random rand = new Random(); + for (int i = 0; i < 50; i++) { + testMaxCapacity(rand.nextInt(1000) + 256); // 256 - 1256 + } + } + + void testMaxCapacity(int maxCapacity) { + Recycler recycler = new Recycler(maxCapacity) { + @Override + protected HandledObject newObject( + Recycler.Handle handle) { + return new HandledObject(handle); + } + }; + + HandledObject[] objects = new HandledObject[maxCapacity * 3]; + for (int i = 0; i < objects.length; i++) { + objects[i] = recycler.get(); + } + + for (int i = 0; i < objects.length; i++) { + recycler.recycle(objects[i], objects[i].handle); + objects[i] = null; + } + + Assert.assertEquals(maxCapacity, recycler.threadLocalCapacity()); + } + + static final class HandledObject { + Recycler.Handle handle; + + HandledObject(Recycler.Handle handle) { + this.handle = handle; + } + } }