diff --git a/buffer/src/main/java/io/netty/buffer/PoolArena.java b/buffer/src/main/java/io/netty/buffer/PoolArena.java index 5cb06249a1..e5d799d486 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolArena.java +++ b/buffer/src/main/java/io/netty/buffer/PoolArena.java @@ -98,12 +98,12 @@ abstract class PoolArena implements PoolArenaMetric { smallSubpagePools[i] = newSubpagePoolHead(pageSize); } - q100 = new PoolChunkList(null, 100, Integer.MAX_VALUE); - q075 = new PoolChunkList(q100, 75, 100); - q050 = new PoolChunkList(q075, 50, 100); - q025 = new PoolChunkList(q050, 25, 75); - q000 = new PoolChunkList(q025, 1, 50); - qInit = new PoolChunkList(q000, Integer.MIN_VALUE, 25); + q100 = new PoolChunkList(null, 100, Integer.MAX_VALUE, chunkSize); + q075 = new PoolChunkList(q100, 75, 100, chunkSize); + q050 = new PoolChunkList(q075, 50, 100, chunkSize); + q025 = new PoolChunkList(q050, 25, 75, chunkSize); + q000 = new PoolChunkList(q025, 1, 50, chunkSize); + qInit = new PoolChunkList(q000, Integer.MIN_VALUE, 25, chunkSize); q100.prevList(q075); q075.prevList(q050); diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunkList.java b/buffer/src/main/java/io/netty/buffer/PoolChunkList.java index da5eb9029c..9573dedddc 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunkList.java +++ b/buffer/src/main/java/io/netty/buffer/PoolChunkList.java @@ -30,6 +30,7 @@ final class PoolChunkList implements PoolChunkListMetric { private final PoolChunkList nextList; private final int minUsage; private final int maxUsage; + private final int maxCapacity; private PoolChunk head; @@ -39,10 +40,32 @@ final class PoolChunkList implements PoolChunkListMetric { // TODO: Test if adding padding helps under contention //private long pad0, pad1, pad2, pad3, pad4, pad5, pad6, pad7; - PoolChunkList(PoolChunkList nextList, int minUsage, int maxUsage) { + PoolChunkList(PoolChunkList nextList, int minUsage, int maxUsage, int chunkSize) { + assert minUsage <= maxUsage; this.nextList = nextList; this.minUsage = minUsage; this.maxUsage = maxUsage; + maxCapacity = calculateMaxCapacity(minUsage, chunkSize); + } + + /** + * Calculates the maximum capacity of a buffer that will ever be possible to allocate out of the {@link PoolChunk}s + * that belong to the {@link PoolChunkList} with the given {@code minUsage} and {@code maxUsage} settings. + */ + private static int calculateMaxCapacity(int minUsage, int chunkSize) { + minUsage = minUsage0(minUsage); + + if (minUsage == 100) { + // If the minUsage is 100 we can not allocate anything out of this list. + return 0; + } + + // Calculate the maximum amount of bytes that can be allocated from a PoolChunk in this PoolChunkList. + // + // As an example: + // - If a PoolChunkList has minUsage == 25 we are allowed to allocate at most 75% of the chunkSize because + // this is the maximum amount available in any PoolChunk in this PoolChunkList. + return (int) (chunkSize * (100L - minUsage) / 100L); } void prevList(PoolChunkList prevList) { @@ -51,7 +74,9 @@ final class PoolChunkList implements PoolChunkListMetric { } boolean allocate(PooledByteBuf buf, int reqCapacity, int normCapacity) { - if (head == null) { + if (head == null || normCapacity > maxCapacity) { + // Either this PoolChunkList is empty or the requested capacity is larger then the capacity which can + // be handled by the PoolChunks that are contained in this PoolChunkList. return false; } @@ -152,7 +177,7 @@ final class PoolChunkList implements PoolChunkListMetric { @Override public int minUsage() { - return max(1, minUsage); + return minUsage0(minUsage); } @Override @@ -160,6 +185,10 @@ final class PoolChunkList implements PoolChunkListMetric { return min(maxUsage, 100); } + private static int minUsage0(int value) { + return max(1, value); + } + @Override public Iterator iterator() { if (head == null) {