diff --git a/buffer/src/main/java/io/netty/buffer/PoolArena.java b/buffer/src/main/java/io/netty/buffer/PoolArena.java index 66da9e3369..b021404e90 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolArena.java +++ b/buffer/src/main/java/io/netty/buffer/PoolArena.java @@ -276,7 +276,7 @@ abstract class PoolArena implements PoolArenaMetric { return; } - freeChunk(chunk, handle, sizeClass, nioBuffer); + freeChunk(chunk, handle, sizeClass, nioBuffer, false); } } @@ -287,21 +287,25 @@ abstract class PoolArena implements PoolArenaMetric { return isTiny(normCapacity) ? SizeClass.Tiny : SizeClass.Small; } - void freeChunk(PoolChunk chunk, long handle, SizeClass sizeClass, ByteBuffer nioBuffer) { + void freeChunk(PoolChunk chunk, long handle, SizeClass sizeClass, ByteBuffer nioBuffer, boolean finalizer) { final boolean destroyChunk; synchronized (this) { - switch (sizeClass) { - case Normal: - ++deallocationsNormal; - break; - case Small: - ++deallocationsSmall; - break; - case Tiny: - ++deallocationsTiny; - break; - default: - throw new Error(); + // We only call this if freeChunk is not called because of the PoolThreadCache finalizer as otherwise this + // may fail due lazy class-loading in for example tomcat. + if (!finalizer) { + switch (sizeClass) { + case Normal: + ++deallocationsNormal; + break; + case Small: + ++deallocationsSmall; + break; + case Tiny: + ++deallocationsTiny; + break; + default: + throw new Error(); + } } destroyChunk = !chunk.parent.free(chunk, handle, nioBuffer); } diff --git a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java index 7e50924113..a42110f384 100644 --- a/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java +++ b/buffer/src/main/java/io/netty/buffer/PoolThreadCache.java @@ -227,23 +227,23 @@ final class PoolThreadCache { try { super.finalize(); } finally { - free(); + free(true); } } /** * Should be called if the Thread that uses this cache is about to exist to release resources out of the cache */ - void free() { + void free(boolean finalizer) { // As free() may be called either by the finalizer or by FastThreadLocal.onRemoval(...) we need to ensure // we only call this one time. if (freed.compareAndSet(false, true)) { - int numFreed = free(tinySubPageDirectCaches) + - free(smallSubPageDirectCaches) + - free(normalDirectCaches) + - free(tinySubPageHeapCaches) + - free(smallSubPageHeapCaches) + - free(normalHeapCaches); + int numFreed = free(tinySubPageDirectCaches, finalizer) + + free(smallSubPageDirectCaches, finalizer) + + free(normalDirectCaches, finalizer) + + free(tinySubPageHeapCaches, finalizer) + + free(smallSubPageHeapCaches, finalizer) + + free(normalHeapCaches, finalizer); if (numFreed > 0 && logger.isDebugEnabled()) { logger.debug("Freed {} thread-local buffer(s) from thread: {}", numFreed, @@ -260,23 +260,23 @@ final class PoolThreadCache { } } - private static int free(MemoryRegionCache[] caches) { + private static int free(MemoryRegionCache[] caches, boolean finalizer) { if (caches == null) { return 0; } int numFreed = 0; for (MemoryRegionCache c: caches) { - numFreed += free(c); + numFreed += free(c, finalizer); } return numFreed; } - private static int free(MemoryRegionCache cache) { + private static int free(MemoryRegionCache cache, boolean finalizer) { if (cache == null) { return 0; } - return cache.free(); + return cache.free(finalizer); } void trim() { @@ -418,16 +418,16 @@ final class PoolThreadCache { /** * Clear out this cache and free up all previous cached {@link PoolChunk}s and {@code handle}s. */ - public final int free() { - return free(Integer.MAX_VALUE); + public final int free(boolean finalizer) { + return free(Integer.MAX_VALUE, finalizer); } - private int free(int max) { + private int free(int max, boolean finalizer) { int numFreed = 0; for (; numFreed < max; numFreed++) { Entry entry = queue.poll(); if (entry != null) { - freeEntry(entry); + freeEntry(entry, finalizer); } else { // all cleared return numFreed; @@ -445,20 +445,23 @@ final class PoolThreadCache { // We not even allocated all the number that are if (free > 0) { - free(free); + free(free, false); } } @SuppressWarnings({ "unchecked", "rawtypes" }) - private void freeEntry(Entry entry) { + private void freeEntry(Entry entry, boolean finalizer) { PoolChunk chunk = entry.chunk; long handle = entry.handle; ByteBuffer nioBuffer = entry.nioBuffer; - // recycle now so PoolChunk can be GC'ed. - entry.recycle(); + if (!finalizer) { + // recycle now so PoolChunk can be GC'ed. This will only be done if this is not freed because of + // a finalizer. + entry.recycle(); + } - chunk.arena.freeChunk(chunk, handle, sizeClass, nioBuffer); + chunk.arena.freeChunk(chunk, handle, sizeClass, nioBuffer, finalizer); } static final class Entry { diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java index 037b2f18d6..8865c15c9d 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java @@ -469,7 +469,7 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator implements @Override protected void onRemoval(PoolThreadCache threadCache) { - threadCache.free(); + threadCache.free(false); } private PoolArena leastUsedArena(PoolArena[] arenas) {