diff --git a/buffer/src/main/java/io/netty/buffer/PoolArena.java b/buffer/src/main/java/io/netty/buffer/PoolArena.java index 7deb7d8962..1e336ce8e9 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolArena.java +++ b/buffer/src/main/java/io/netty/buffer/PoolArena.java @@ -455,6 +455,22 @@ abstract class PoolArena extends SizeClasses implements PoolArenaMetric { return max(0, val); } + /** + * Return the number of bytes that are currently pinned to buffer instances, by the arena. The pinned memory is not + * accessible for use by any other allocation, until the buffers using have all been released. + */ + public long numPinnedBytes() { + long val = activeBytesHuge.longValue(); // Huge chunks are exact-sized for the buffers they were allocated to. + synchronized (this) { + for (int i = 0; i < chunkListMetrics.size(); i++) { + for (PoolChunkMetric m: chunkListMetrics.get(i)) { + val += ((PoolChunk) m).pinnedBytes(); + } + } + } + return max(0, val); + } + protected abstract PoolChunk newChunk(int pageSize, int maxPageIdx, int pageShifts, int chunkSize); protected abstract PoolChunk newUnpooledChunk(int capacity); protected abstract PooledByteBuf newByteBuf(int maxCapacity); diff --git a/buffer/src/main/java/io/netty/buffer/PoolChunk.java b/buffer/src/main/java/io/netty/buffer/PoolChunk.java index 9fb4bde7f9..9b84803794 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolChunk.java +++ b/buffer/src/main/java/io/netty/buffer/PoolChunk.java @@ -175,6 +175,7 @@ final class PoolChunk implements PoolChunkMetric { private final Deque cachedNioBuffers; int freeBytes; + int pinnedBytes; PoolChunkList parent; PoolChunk prev; @@ -342,7 +343,9 @@ final class PoolChunk implements PoolChunkMetric { handle = splitLargeRun(handle, pages); } - freeBytes -= runSize(pageShifts, handle); + int pinnedSize = runSize(pageShifts, handle); + freeBytes -= pinnedSize; + pinnedBytes += pinnedSize; return handle; } } @@ -451,6 +454,8 @@ final class PoolChunk implements PoolChunkMetric { * @param handle handle to free */ void free(long handle, int normCapacity, ByteBuffer nioBuffer) { + int runSize = runSize(pageShifts, handle); + pinnedBytes -= runSize; if (isSubpage(handle)) { int sizeIdx = arena.size2SizeIdx(normCapacity); PoolSubpage head = arena.findSubpagePoolHead(sizeIdx); @@ -473,8 +478,6 @@ final class PoolChunk implements PoolChunkMetric { } //start free run - int pages = runPages(handle); - synchronized (runsAvail) { // collapse continuous runs, successfully collapsed runs // will be removed from runsAvail and runsAvailMap @@ -486,7 +489,7 @@ final class PoolChunk implements PoolChunkMetric { finalRun &= ~(1L << IS_SUBPAGE_SHIFT); insertAvailRun(runOffset(finalRun), runPages(finalRun), finalRun); - freeBytes += pages << pageShifts; + freeBytes += runSize; } if (nioBuffer != null && cachedNioBuffers != null && @@ -588,6 +591,12 @@ final class PoolChunk implements PoolChunkMetric { } } + public int pinnedBytes() { + synchronized (arena) { + return pinnedBytes; + } + } + @Override public String toString() { final int freeBytes; diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java index 9fb469cd97..a6caa66f20 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java @@ -658,6 +658,40 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements return used; } + /** + * Returns the number of bytes of heap memory that is currently pinned to heap buffers allocated by a + * {@link ByteBufAllocator}, or {@code -1} if unknown. + * A buffer can pin more memory than its {@linkplain ByteBuf#capacity() capacity} might indicate, + * due to implementation details of the allocator. + */ + public final long pinnedHeapMemory() { + return pinnedMemory(heapArenas); + } + + /** + * Returns the number of bytes of direct memory that is currently pinned to direct buffers allocated by a + * {@link ByteBufAllocator}, or {@code -1} if unknown. + * A buffer can pin more memory than its {@linkplain ByteBuf#capacity() capacity} might indicate, + * due to implementation details of the allocator. + */ + public final long pinnedDirectMemory() { + return pinnedMemory(directArenas); + } + + private static long pinnedMemory(PoolArena[] arenas) { + if (arenas == null) { + return -1; + } + long used = 0; + for (PoolArena arena : arenas) { + used += arena.numPinnedBytes(); + if (used < 0) { + return Long.MAX_VALUE; + } + } + return used; + } + final PoolThreadCache threadCache() { PoolThreadCache cache = threadCache.get(); assert cache != null; diff --git a/buffer/src/test/java/io/netty/buffer/AbstractByteBufAllocatorTest.java b/buffer/src/test/java/io/netty/buffer/AbstractByteBufAllocatorTest.java index 3aac818458..e6e064b40c 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractByteBufAllocatorTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractByteBufAllocatorTest.java @@ -139,4 +139,7 @@ public abstract class AbstractByteBufAllocatorTest