From 65b522a2a75c78c977a65e15663509509b5f12e0 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Wed, 4 Dec 2013 19:03:32 +0900 Subject: [PATCH] Better buffer leak reporting - Remove the reference to ResourceLeak from the buffer implementations and use wrappers instead: - SimpleLeakAwareByteBuf and AdvancedLeakAwareByteBuf - It is now allocator's responsibility to create a leak-aware buffer. - Added AbstractByteBufAllocator.toLeakAwareBuffer() for easier implementation - Add WrappedByteBuf to reduce duplication between *LeakAwareByteBuf and UnreleasableByteBuf - Raise the level of leak reports to ERROR - because it will break the app eventually - Replace enabled/disabled property with the leak detection level - Only print stack trace when level is ADVANCED or above to avoid user confusion - Add the 'leak' build profile, which enables highly detailed leak reporting during the build - Remove ResourceLeakException which is unsed anymore --- .../buffer/AbstractByteBufAllocator.java | 22 + .../buffer/AdvancedLeakAwareByteBuf.java | 691 +++++++++++++++ .../java/io/netty/buffer/PooledByteBuf.java | 9 +- .../netty/buffer/PooledByteBufAllocator.java | 18 +- .../buffer/PooledUnsafeDirectByteBuf.java | 10 +- .../netty/buffer/ReadOnlyByteBufferBuf.java | 9 +- .../buffer/ReadOnlyUnsafeDirectByteBuf.java | 10 +- .../netty/buffer/SimpleLeakAwareByteBuf.java | 47 + .../buffer/UnpooledByteBufAllocator.java | 7 +- .../netty/buffer/UnpooledDirectByteBuf.java | 15 +- .../buffer/UnpooledUnsafeDirectByteBuf.java | 18 +- .../io/netty/buffer/UnreleasableByteBuf.java | 756 +--------------- .../java/io/netty/buffer/WrappedByteBuf.java | 826 ++++++++++++++++++ .../main/java/io/netty/util/ResourceLeak.java | 6 + .../io/netty/util/ResourceLeakDetector.java | 196 ++++- .../io/netty/util/ResourceLeakException.java | 67 -- pom.xml | 22 +- 17 files changed, 1822 insertions(+), 907 deletions(-) create mode 100644 buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java create mode 100644 buffer/src/main/java/io/netty/buffer/SimpleLeakAwareByteBuf.java create mode 100644 buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java delete mode 100644 common/src/main/java/io/netty/util/ResourceLeakException.java diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java index b4c1d62444..4d39bc29ee 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBufAllocator.java @@ -16,6 +16,8 @@ package io.netty.buffer; +import io.netty.util.ResourceLeak; +import io.netty.util.ResourceLeakDetector; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.StringUtil; @@ -26,6 +28,26 @@ public abstract class AbstractByteBufAllocator implements ByteBufAllocator { private static final int DEFAULT_INITIAL_CAPACITY = 256; private static final int DEFAULT_MAX_COMPONENTS = 16; + protected static ByteBuf toLeakAwareBuffer(ByteBuf buf) { + ResourceLeak leak; + switch (ResourceLeakDetector.getLevel()) { + case SIMPLE: + leak = AbstractByteBuf.leakDetector.open(buf); + if (leak != null) { + buf = new SimpleLeakAwareByteBuf(buf, leak); + } + break; + case ADVANCED: + case PARANOID: + leak = AbstractByteBuf.leakDetector.open(buf); + if (leak != null) { + buf = new AdvancedLeakAwareByteBuf(buf, leak); + } + break; + } + return buf; + } + private final boolean directByDefault; private final ByteBuf emptyBuf; diff --git a/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java new file mode 100644 index 0000000000..e862b9eebc --- /dev/null +++ b/buffer/src/main/java/io/netty/buffer/AdvancedLeakAwareByteBuf.java @@ -0,0 +1,691 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.buffer; + +import io.netty.util.ResourceLeak; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; +import java.nio.charset.Charset; + +final class AdvancedLeakAwareByteBuf extends WrappedByteBuf { + + private final ResourceLeak leak; + + AdvancedLeakAwareByteBuf(ByteBuf buf, ResourceLeak leak) { + super(buf); + this.leak = leak; + } + + @Override + public ByteBuf discardReadBytes() { + leak.record(); + return super.discardReadBytes(); + } + + @Override + public ByteBuf discardSomeReadBytes() { + leak.record(); + return super.discardSomeReadBytes(); + } + + @Override + public ByteBuf ensureWritable(int minWritableBytes) { + leak.record(); + return super.ensureWritable(minWritableBytes); + } + + @Override + public int ensureWritable(int minWritableBytes, boolean force) { + leak.record(); + return super.ensureWritable(minWritableBytes, force); + } + + @Override + public boolean getBoolean(int index) { + leak.record(); + return super.getBoolean(index); + } + + @Override + public byte getByte(int index) { + leak.record(); + return super.getByte(index); + } + + @Override + public short getUnsignedByte(int index) { + leak.record(); + return super.getUnsignedByte(index); + } + + @Override + public short getShort(int index) { + leak.record(); + return super.getShort(index); + } + + @Override + public int getUnsignedShort(int index) { + leak.record(); + return super.getUnsignedShort(index); + } + + @Override + public int getMedium(int index) { + leak.record(); + return super.getMedium(index); + } + + @Override + public int getUnsignedMedium(int index) { + leak.record(); + return super.getUnsignedMedium(index); + } + + @Override + public int getInt(int index) { + leak.record(); + return super.getInt(index); + } + + @Override + public long getUnsignedInt(int index) { + leak.record(); + return super.getUnsignedInt(index); + } + + @Override + public long getLong(int index) { + leak.record(); + return super.getLong(index); + } + + @Override + public char getChar(int index) { + leak.record(); + return super.getChar(index); + } + + @Override + public float getFloat(int index) { + leak.record(); + return super.getFloat(index); + } + + @Override + public double getDouble(int index) { + leak.record(); + return super.getDouble(index); + } + + @Override + public ByteBuf getBytes(int index, ByteBuf dst) { + leak.record(); + return super.getBytes(index, dst); + } + + @Override + public ByteBuf getBytes(int index, ByteBuf dst, int length) { + leak.record(); + return super.getBytes(index, dst, length); + } + + @Override + public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) { + leak.record(); + return super.getBytes(index, dst, dstIndex, length); + } + + @Override + public ByteBuf getBytes(int index, byte[] dst) { + leak.record(); + return super.getBytes(index, dst); + } + + @Override + public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) { + leak.record(); + return super.getBytes(index, dst, dstIndex, length); + } + + @Override + public ByteBuf getBytes(int index, ByteBuffer dst) { + leak.record(); + return super.getBytes(index, dst); + } + + @Override + public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException { + leak.record(); + return super.getBytes(index, out, length); + } + + @Override + public int getBytes(int index, GatheringByteChannel out, int length) throws IOException { + leak.record(); + return super.getBytes(index, out, length); + } + + @Override + public ByteBuf setBoolean(int index, boolean value) { + leak.record(); + return super.setBoolean(index, value); + } + + @Override + public ByteBuf setByte(int index, int value) { + leak.record(); + return super.setByte(index, value); + } + + @Override + public ByteBuf setShort(int index, int value) { + leak.record(); + return super.setShort(index, value); + } + + @Override + public ByteBuf setMedium(int index, int value) { + leak.record(); + return super.setMedium(index, value); + } + + @Override + public ByteBuf setInt(int index, int value) { + leak.record(); + return super.setInt(index, value); + } + + @Override + public ByteBuf setLong(int index, long value) { + leak.record(); + return super.setLong(index, value); + } + + @Override + public ByteBuf setChar(int index, int value) { + leak.record(); + return super.setChar(index, value); + } + + @Override + public ByteBuf setFloat(int index, float value) { + leak.record(); + return super.setFloat(index, value); + } + + @Override + public ByteBuf setDouble(int index, double value) { + leak.record(); + return super.setDouble(index, value); + } + + @Override + public ByteBuf setBytes(int index, ByteBuf src) { + leak.record(); + return super.setBytes(index, src); + } + + @Override + public ByteBuf setBytes(int index, ByteBuf src, int length) { + leak.record(); + return super.setBytes(index, src, length); + } + + @Override + public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) { + leak.record(); + return super.setBytes(index, src, srcIndex, length); + } + + @Override + public ByteBuf setBytes(int index, byte[] src) { + leak.record(); + return super.setBytes(index, src); + } + + @Override + public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) { + leak.record(); + return super.setBytes(index, src, srcIndex, length); + } + + @Override + public ByteBuf setBytes(int index, ByteBuffer src) { + leak.record(); + return super.setBytes(index, src); + } + + @Override + public int setBytes(int index, InputStream in, int length) throws IOException { + leak.record(); + return super.setBytes(index, in, length); + } + + @Override + public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException { + leak.record(); + return super.setBytes(index, in, length); + } + + @Override + public ByteBuf setZero(int index, int length) { + leak.record(); + return super.setZero(index, length); + } + + @Override + public boolean readBoolean() { + leak.record(); + return super.readBoolean(); + } + + @Override + public byte readByte() { + leak.record(); + return super.readByte(); + } + + @Override + public short readUnsignedByte() { + leak.record(); + return super.readUnsignedByte(); + } + + @Override + public short readShort() { + leak.record(); + return super.readShort(); + } + + @Override + public int readUnsignedShort() { + leak.record(); + return super.readUnsignedShort(); + } + + @Override + public int readMedium() { + leak.record(); + return super.readMedium(); + } + + @Override + public int readUnsignedMedium() { + leak.record(); + return super.readUnsignedMedium(); + } + + @Override + public int readInt() { + leak.record(); + return super.readInt(); + } + + @Override + public long readUnsignedInt() { + leak.record(); + return super.readUnsignedInt(); + } + + @Override + public long readLong() { + leak.record(); + return super.readLong(); + } + + @Override + public char readChar() { + leak.record(); + return super.readChar(); + } + + @Override + public float readFloat() { + leak.record(); + return super.readFloat(); + } + + @Override + public double readDouble() { + leak.record(); + return super.readDouble(); + } + + @Override + public ByteBuf readBytes(int length) { + leak.record(); + return super.readBytes(length); + } + + @Override + public ByteBuf readSlice(int length) { + leak.record(); + return super.readSlice(length); + } + + @Override + public ByteBuf readBytes(ByteBuf dst) { + leak.record(); + return super.readBytes(dst); + } + + @Override + public ByteBuf readBytes(ByteBuf dst, int length) { + leak.record(); + return super.readBytes(dst, length); + } + + @Override + public ByteBuf readBytes(ByteBuf dst, int dstIndex, int length) { + leak.record(); + return super.readBytes(dst, dstIndex, length); + } + + @Override + public ByteBuf readBytes(byte[] dst) { + leak.record(); + return super.readBytes(dst); + } + + @Override + public ByteBuf readBytes(byte[] dst, int dstIndex, int length) { + leak.record(); + return super.readBytes(dst, dstIndex, length); + } + + @Override + public ByteBuf readBytes(ByteBuffer dst) { + leak.record(); + return super.readBytes(dst); + } + + @Override + public ByteBuf readBytes(OutputStream out, int length) throws IOException { + leak.record(); + return super.readBytes(out, length); + } + + @Override + public int readBytes(GatheringByteChannel out, int length) throws IOException { + leak.record(); + return super.readBytes(out, length); + } + + @Override + public ByteBuf skipBytes(int length) { + leak.record(); + return super.skipBytes(length); + } + + @Override + public ByteBuf writeBoolean(boolean value) { + leak.record(); + return super.writeBoolean(value); + } + + @Override + public ByteBuf writeByte(int value) { + leak.record(); + return super.writeByte(value); + } + + @Override + public ByteBuf writeShort(int value) { + leak.record(); + return super.writeShort(value); + } + + @Override + public ByteBuf writeMedium(int value) { + leak.record(); + return super.writeMedium(value); + } + + @Override + public ByteBuf writeInt(int value) { + leak.record(); + return super.writeInt(value); + } + + @Override + public ByteBuf writeLong(long value) { + leak.record(); + return super.writeLong(value); + } + + @Override + public ByteBuf writeChar(int value) { + leak.record(); + return super.writeChar(value); + } + + @Override + public ByteBuf writeFloat(float value) { + leak.record(); + return super.writeFloat(value); + } + + @Override + public ByteBuf writeDouble(double value) { + leak.record(); + return super.writeDouble(value); + } + + @Override + public ByteBuf writeBytes(ByteBuf src) { + leak.record(); + return super.writeBytes(src); + } + + @Override + public ByteBuf writeBytes(ByteBuf src, int length) { + leak.record(); + return super.writeBytes(src, length); + } + + @Override + public ByteBuf writeBytes(ByteBuf src, int srcIndex, int length) { + leak.record(); + return super.writeBytes(src, srcIndex, length); + } + + @Override + public ByteBuf writeBytes(byte[] src) { + leak.record(); + return super.writeBytes(src); + } + + @Override + public ByteBuf writeBytes(byte[] src, int srcIndex, int length) { + leak.record(); + return super.writeBytes(src, srcIndex, length); + } + + @Override + public ByteBuf writeBytes(ByteBuffer src) { + leak.record(); + return super.writeBytes(src); + } + + @Override + public int writeBytes(InputStream in, int length) throws IOException { + leak.record(); + return super.writeBytes(in, length); + } + + @Override + public int writeBytes(ScatteringByteChannel in, int length) throws IOException { + leak.record(); + return super.writeBytes(in, length); + } + + @Override + public ByteBuf writeZero(int length) { + leak.record(); + return super.writeZero(length); + } + + @Override + public int indexOf(int fromIndex, int toIndex, byte value) { + leak.record(); + return super.indexOf(fromIndex, toIndex, value); + } + + @Override + public int bytesBefore(byte value) { + leak.record(); + return super.bytesBefore(value); + } + + @Override + public int bytesBefore(int length, byte value) { + leak.record(); + return super.bytesBefore(length, value); + } + + @Override + public int bytesBefore(int index, int length, byte value) { + leak.record(); + return super.bytesBefore(index, length, value); + } + + @Override + public int forEachByte(ByteBufProcessor processor) { + leak.record(); + return super.forEachByte(processor); + } + + @Override + public int forEachByte(int index, int length, ByteBufProcessor processor) { + leak.record(); + return super.forEachByte(index, length, processor); + } + + @Override + public int forEachByteDesc(ByteBufProcessor processor) { + leak.record(); + return super.forEachByteDesc(processor); + } + + @Override + public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + leak.record(); + return super.forEachByteDesc(index, length, processor); + } + + @Override + public ByteBuf copy() { + leak.record(); + return super.copy(); + } + + @Override + public ByteBuf copy(int index, int length) { + leak.record(); + return super.copy(index, length); + } + + @Override + public ByteBuf slice() { + leak.record(); + return super.slice(); + } + + @Override + public ByteBuf slice(int index, int length) { + leak.record(); + return super.slice(index, length); + } + + @Override + public ByteBuf duplicate() { + leak.record(); + return super.duplicate(); + } + + @Override + public int nioBufferCount() { + leak.record(); + return super.nioBufferCount(); + } + + @Override + public ByteBuffer nioBuffer() { + leak.record(); + return super.nioBuffer(); + } + + @Override + public ByteBuffer nioBuffer(int index, int length) { + leak.record(); + return super.nioBuffer(index, length); + } + + @Override + public ByteBuffer[] nioBuffers() { + leak.record(); + return super.nioBuffers(); + } + + @Override + public ByteBuffer[] nioBuffers(int index, int length) { + leak.record(); + return super.nioBuffers(index, length); + } + + @Override + public ByteBuffer internalNioBuffer(int index, int length) { + leak.record(); + return super.internalNioBuffer(index, length); + } + + @Override + public String toString(Charset charset) { + leak.record(); + return super.toString(charset); + } + + @Override + public String toString(int index, int length, Charset charset) { + leak.record(); + return super.toString(index, length, charset); + } + + @Override + public boolean release() { + boolean deallocated = super.release(); + if (deallocated) { + leak.close(); + } + return deallocated; + } + + @Override + public boolean release(int decrement) { + boolean deallocated = super.release(decrement); + if (deallocated) { + leak.close(); + } + return deallocated; + } +} diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java index 5446185eec..3f88a69713 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java @@ -17,14 +17,12 @@ package io.netty.buffer; import io.netty.util.Recycler; -import io.netty.util.ResourceLeak; import java.nio.ByteBuffer; import java.nio.ByteOrder; abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { - private final ResourceLeak leak; private final Recycler.Handle recyclerHandle; protected PoolChunk chunk; @@ -38,7 +36,6 @@ abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { protected PooledByteBuf(Recycler.Handle recyclerHandle, int maxCapacity) { super(maxCapacity); - leak = leakDetector.open(this); this.recyclerHandle = recyclerHandle; } @@ -144,11 +141,7 @@ abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { this.handle = -1; memory = null; chunk.arena.free(chunk, handle); - if (leak != null) { - leak.close(); - } else { - recycle(); - } + recycle(); } } diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java index 6cb10310c3..cbe4252102 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBufAllocator.java @@ -217,26 +217,34 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator { protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) { PoolThreadCache cache = threadCache.get(); PoolArena heapArena = cache.heapArena; + + ByteBuf buf; if (heapArena != null) { - return heapArena.allocate(cache, initialCapacity, maxCapacity); + buf = heapArena.allocate(cache, initialCapacity, maxCapacity); } else { - return new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity); + buf = new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity); } + + return toLeakAwareBuffer(buf); } @Override protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) { PoolThreadCache cache = threadCache.get(); PoolArena directArena = cache.directArena; + + ByteBuf buf; if (directArena != null) { - return directArena.allocate(cache, initialCapacity, maxCapacity); + buf = directArena.allocate(cache, initialCapacity, maxCapacity); } else { if (PlatformDependent.hasUnsafe()) { - return new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity); + buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity); } else { - return new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity); + buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity); } } + + return toLeakAwareBuffer(buf); } @Override diff --git a/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java index 2ed0437210..92bd6ef423 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledUnsafeDirectByteBuf.java @@ -317,10 +317,14 @@ final class PooledUnsafeDirectByteBuf extends PooledByteBuf { @Override public ByteBuf copy(int index, int length) { checkIndex(index, length); - PooledUnsafeDirectByteBuf copy = (PooledUnsafeDirectByteBuf) alloc().directBuffer(length, maxCapacity()); + ByteBuf copy = alloc().directBuffer(length, maxCapacity()); if (length != 0) { - PlatformDependent.copyMemory(addr(index), copy.addr(0), length); - copy.setIndex(0, length); + if (copy.hasMemoryAddress()) { + PlatformDependent.copyMemory(addr(index), copy.memoryAddress(), length); + copy.setIndex(0, length); + } else { + copy.writeBytes(this, index, length); + } } return copy; } diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java index 47efe9f1ce..47e4eb7473 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java @@ -15,7 +15,6 @@ */ package io.netty.buffer; -import io.netty.util.ResourceLeak; import io.netty.util.internal.StringUtil; import java.io.IOException; @@ -32,7 +31,6 @@ import java.nio.channels.ScatteringByteChannel; * Read-only ByteBuf which wraps a read-only ByteBuffer. */ class ReadOnlyByteBufferBuf extends AbstractReferenceCountedByteBuf { - private final ResourceLeak leak; protected final ByteBuffer buffer; private final ByteBufAllocator allocator; @@ -47,15 +45,10 @@ class ReadOnlyByteBufferBuf extends AbstractReferenceCountedByteBuf { this.allocator = allocator; this.buffer = buffer.slice().order(ByteOrder.BIG_ENDIAN); writerIndex(this.buffer.limit()); - leak = leakDetector.open(this); } @Override - protected void deallocate() { - if (leak != null) { - leak.close(); - } - } + protected void deallocate() { } @Override public byte getByte(int index) { diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyUnsafeDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/ReadOnlyUnsafeDirectByteBuf.java index 6f17f59e52..7093b4f28b 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyUnsafeDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ReadOnlyUnsafeDirectByteBuf.java @@ -120,10 +120,14 @@ final class ReadOnlyUnsafeDirectByteBuf extends ReadOnlyByteBufferBuf { @Override public ByteBuf copy(int index, int length) { checkIndex(index, length); - UnpooledUnsafeDirectByteBuf copy = (UnpooledUnsafeDirectByteBuf) alloc().directBuffer(length, maxCapacity()); + ByteBuf copy = alloc().directBuffer(length, maxCapacity()); if (length != 0) { - PlatformDependent.copyMemory(addr(index), copy.addr(0), length); - copy.setIndex(0, length); + if (copy.hasMemoryAddress()) { + PlatformDependent.copyMemory(addr(index), copy.memoryAddress(), length); + copy.setIndex(0, length); + } else { + copy.writeBytes(this, index, length); + } } return copy; } diff --git a/buffer/src/main/java/io/netty/buffer/SimpleLeakAwareByteBuf.java b/buffer/src/main/java/io/netty/buffer/SimpleLeakAwareByteBuf.java new file mode 100644 index 0000000000..e2061a8fd8 --- /dev/null +++ b/buffer/src/main/java/io/netty/buffer/SimpleLeakAwareByteBuf.java @@ -0,0 +1,47 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.buffer; + +import io.netty.util.ResourceLeak; + +final class SimpleLeakAwareByteBuf extends WrappedByteBuf { + + private final ResourceLeak leak; + + SimpleLeakAwareByteBuf(ByteBuf buf, ResourceLeak leak) { + super(buf); + this.leak = leak; + } + + @Override + public boolean release() { + boolean deallocated = super.release(); + if (deallocated) { + leak.close(); + } + return deallocated; + } + + @Override + public boolean release(int decrement) { + boolean deallocated = super.release(decrement); + if (deallocated) { + leak.close(); + } + return deallocated; + } +} diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java b/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java index 90fcb10b15..e38e40b308 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledByteBufAllocator.java @@ -45,11 +45,14 @@ public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator { @Override protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) { + ByteBuf buf; if (PlatformDependent.hasUnsafe()) { - return new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity); + buf = new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity); } else { - return new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity); + buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity); } + + return toLeakAwareBuffer(buf); } @Override diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java index a61d3c5eac..844479e4dd 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java @@ -15,7 +15,6 @@ */ package io.netty.buffer; -import io.netty.util.ResourceLeak; import io.netty.util.internal.PlatformDependent; import java.io.IOException; @@ -34,7 +33,6 @@ import java.nio.channels.ScatteringByteChannel; */ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf { - private final ResourceLeak leak; private final ByteBufAllocator alloc; private ByteBuffer buffer; @@ -66,7 +64,6 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf { this.alloc = alloc; setByteBuffer(ByteBuffer.allocateDirect(initialCapacity)); - leak = leakDetector.open(this); } /** @@ -99,7 +96,6 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf { doNotFree = true; setByteBuffer(initialBuffer.slice().order(ByteOrder.BIG_ENDIAN)); writerIndex(initialCapacity); - leak = leakDetector.open(this); } /** @@ -566,12 +562,7 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf { throw new IndexOutOfBoundsException("Too many bytes to read - Need " + (index + length)); } - ByteBuffer dst = - src.isDirect()? allocateDirect(length) : ByteBuffer.allocate(length); - dst.put(src); - dst.order(order()); - dst.clear(); - return new UnpooledDirectByteBuf(alloc(), dst, maxCapacity()); + return alloc().directBuffer(length, maxCapacity()).writeBytes(src); } @Override @@ -604,10 +595,6 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf { if (!doNotFree) { freeDirect(buffer); } - - if (leak != null) { - leak.close(); - } } @Override diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java index 50fdf02aee..89df31acde 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java @@ -15,7 +15,6 @@ */ package io.netty.buffer; -import io.netty.util.ResourceLeak; import io.netty.util.internal.PlatformDependent; import java.io.IOException; @@ -36,7 +35,6 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf private static final boolean NATIVE_ORDER = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; - private final ResourceLeak leak; private final ByteBufAllocator alloc; private long memoryAddress; @@ -69,7 +67,6 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf this.alloc = alloc; setByteBuffer(allocateDirect(initialCapacity)); - leak = leakDetector.open(this); } /** @@ -102,7 +99,6 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf doNotFree = true; setByteBuffer(initialBuffer.slice().order(ByteOrder.BIG_ENDIAN)); writerIndex(initialCapacity); - leak = leakDetector.open(this); } /** @@ -466,10 +462,14 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf @Override public ByteBuf copy(int index, int length) { checkIndex(index, length); - UnpooledUnsafeDirectByteBuf copy = (UnpooledUnsafeDirectByteBuf) alloc().directBuffer(length, maxCapacity()); + ByteBuf copy = alloc().directBuffer(length, maxCapacity()); if (length != 0) { - PlatformDependent.copyMemory(addr(index), copy.addr(0), length); - copy.setIndex(0, length); + if (copy.hasMemoryAddress()) { + PlatformDependent.copyMemory(addr(index), copy.memoryAddress(), length); + copy.setIndex(0, length); + } else { + copy.writeBytes(this, index, length); + } } return copy; } @@ -504,10 +504,6 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf if (!doNotFree) { freeDirect(buffer); } - - if (leak != null) { - leak.close(); - } } @Override diff --git a/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java index 274f94822f..8310ea03c2 100644 --- a/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java @@ -15,67 +15,18 @@ */ package io.netty.buffer; -import io.netty.util.internal.StringUtil; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.nio.channels.GatheringByteChannel; -import java.nio.channels.ScatteringByteChannel; -import java.nio.charset.Charset; /** * A {@link ByteBuf} implementation that wraps another buffer to prevent a user from increasing or decreasing the * wrapped buffer's reference count. */ -final class UnreleasableByteBuf extends ByteBuf { +final class UnreleasableByteBuf extends WrappedByteBuf { - private final ByteBuf buf; private SwappedByteBuf swappedBuf; UnreleasableByteBuf(ByteBuf buf) { - if (buf == null) { - throw new NullPointerException("buf"); - } - this.buf = buf; - } - - @Override - public boolean hasMemoryAddress() { - return buf.hasMemoryAddress(); - } - - @Override - public long memoryAddress() { - return buf.memoryAddress(); - } - - @Override - public int capacity() { - return buf.capacity(); - } - - @Override - public ByteBuf capacity(int newCapacity) { - buf.capacity(newCapacity); - return this; - } - - @Override - public int maxCapacity() { - return buf.maxCapacity(); - } - - @Override - public ByteBufAllocator alloc() { - return buf.alloc(); - } - - @Override - public ByteOrder order() { - return buf.order(); + super(buf); } @Override @@ -94,624 +45,11 @@ final class UnreleasableByteBuf extends ByteBuf { return swappedBuf; } - @Override - public ByteBuf unwrap() { - return buf; - } - - @Override - public boolean isDirect() { - return buf.isDirect(); - } - - @Override - public int readerIndex() { - return buf.readerIndex(); - } - - @Override - public ByteBuf readerIndex(int readerIndex) { - buf.readerIndex(readerIndex); - return this; - } - - @Override - public int writerIndex() { - return buf.writerIndex(); - } - - @Override - public ByteBuf writerIndex(int writerIndex) { - buf.writerIndex(writerIndex); - return this; - } - - @Override - public ByteBuf setIndex(int readerIndex, int writerIndex) { - buf.setIndex(readerIndex, writerIndex); - return this; - } - - @Override - public int readableBytes() { - return buf.readableBytes(); - } - - @Override - public int writableBytes() { - return buf.writableBytes(); - } - - @Override - public int maxWritableBytes() { - return buf.maxWritableBytes(); - } - - @Override - public boolean isReadable() { - return buf.isReadable(); - } - - @Override - public boolean isWritable() { - return buf.isWritable(); - } - - @Override - public ByteBuf clear() { - buf.clear(); - return this; - } - - @Override - public ByteBuf markReaderIndex() { - buf.markReaderIndex(); - return this; - } - - @Override - public ByteBuf resetReaderIndex() { - buf.resetReaderIndex(); - return this; - } - - @Override - public ByteBuf markWriterIndex() { - buf.markWriterIndex(); - return this; - } - - @Override - public ByteBuf resetWriterIndex() { - buf.resetWriterIndex(); - return this; - } - - @Override - public ByteBuf discardReadBytes() { - buf.discardReadBytes(); - return this; - } - - @Override - public ByteBuf discardSomeReadBytes() { - buf.discardSomeReadBytes(); - return this; - } - - @Override - public ByteBuf ensureWritable(int minWritableBytes) { - buf.ensureWritable(minWritableBytes); - return this; - } - - @Override - public int ensureWritable(int minWritableBytes, boolean force) { - return buf.ensureWritable(minWritableBytes, force); - } - - @Override - public boolean getBoolean(int index) { - return buf.getBoolean(index); - } - - @Override - public byte getByte(int index) { - return buf.getByte(index); - } - - @Override - public short getUnsignedByte(int index) { - return buf.getUnsignedByte(index); - } - - @Override - public short getShort(int index) { - return buf.getShort(index); - } - - @Override - public int getUnsignedShort(int index) { - return buf.getUnsignedShort(index); - } - - @Override - public int getMedium(int index) { - return buf.getMedium(index); - } - - @Override - public int getUnsignedMedium(int index) { - return buf.getUnsignedMedium(index); - } - - @Override - public int getInt(int index) { - return buf.getInt(index); - } - - @Override - public long getUnsignedInt(int index) { - return buf.getUnsignedInt(index); - } - - @Override - public long getLong(int index) { - return buf.getLong(index); - } - - @Override - public char getChar(int index) { - return buf.getChar(index); - } - - @Override - public float getFloat(int index) { - return buf.getFloat(index); - } - - @Override - public double getDouble(int index) { - return buf.getDouble(index); - } - - @Override - public ByteBuf getBytes(int index, ByteBuf dst) { - buf.getBytes(index, dst); - return this; - } - - @Override - public ByteBuf getBytes(int index, ByteBuf dst, int length) { - buf.getBytes(index, dst, length); - return this; - } - - @Override - public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) { - buf.getBytes(index, dst, dstIndex, length); - return this; - } - - @Override - public ByteBuf getBytes(int index, byte[] dst) { - buf.getBytes(index, dst); - return this; - } - - @Override - public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) { - buf.getBytes(index, dst, dstIndex, length); - return this; - } - - @Override - public ByteBuf getBytes(int index, ByteBuffer dst) { - buf.getBytes(index, dst); - return this; - } - - @Override - public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException { - buf.getBytes(index, out, length); - return this; - } - - @Override - public int getBytes(int index, GatheringByteChannel out, int length) throws IOException { - return buf.getBytes(index, out, length); - } - - @Override - public ByteBuf setBoolean(int index, boolean value) { - buf.setBoolean(index, value); - return this; - } - - @Override - public ByteBuf setByte(int index, int value) { - buf.setByte(index, value); - return this; - } - - @Override - public ByteBuf setShort(int index, int value) { - buf.setShort(index, value); - return this; - } - - @Override - public ByteBuf setMedium(int index, int value) { - buf.setMedium(index, value); - return this; - } - - @Override - public ByteBuf setInt(int index, int value) { - buf.setInt(index, value); - return this; - } - - @Override - public ByteBuf setLong(int index, long value) { - buf.setLong(index, value); - return this; - } - - @Override - public ByteBuf setChar(int index, int value) { - buf.setChar(index, value); - return this; - } - - @Override - public ByteBuf setFloat(int index, float value) { - buf.setFloat(index, value); - return this; - } - - @Override - public ByteBuf setDouble(int index, double value) { - buf.setDouble(index, value); - return this; - } - - @Override - public ByteBuf setBytes(int index, ByteBuf src) { - buf.setBytes(index, src); - return this; - } - - @Override - public ByteBuf setBytes(int index, ByteBuf src, int length) { - buf.setBytes(index, src, length); - return this; - } - - @Override - public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) { - buf.setBytes(index, src, srcIndex, length); - return this; - } - - @Override - public ByteBuf setBytes(int index, byte[] src) { - buf.setBytes(index, src); - return this; - } - - @Override - public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) { - buf.setBytes(index, src, srcIndex, length); - return this; - } - - @Override - public ByteBuf setBytes(int index, ByteBuffer src) { - buf.setBytes(index, src); - return this; - } - - @Override - public int setBytes(int index, InputStream in, int length) throws IOException { - return buf.setBytes(index, in, length); - } - - @Override - public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException { - return buf.setBytes(index, in, length); - } - - @Override - public ByteBuf setZero(int index, int length) { - buf.setZero(index, length); - return this; - } - - @Override - public boolean readBoolean() { - return buf.readBoolean(); - } - - @Override - public byte readByte() { - return buf.readByte(); - } - - @Override - public short readUnsignedByte() { - return buf.readUnsignedByte(); - } - - @Override - public short readShort() { - return buf.readShort(); - } - - @Override - public int readUnsignedShort() { - return buf.readUnsignedShort(); - } - - @Override - public int readMedium() { - return buf.readMedium(); - } - - @Override - public int readUnsignedMedium() { - return buf.readUnsignedMedium(); - } - - @Override - public int readInt() { - return buf.readInt(); - } - - @Override - public long readUnsignedInt() { - return buf.readUnsignedInt(); - } - - @Override - public long readLong() { - return buf.readLong(); - } - - @Override - public char readChar() { - return buf.readChar(); - } - - @Override - public float readFloat() { - return buf.readFloat(); - } - - @Override - public double readDouble() { - return buf.readDouble(); - } - - @Override - public ByteBuf readBytes(int length) { - return buf.readBytes(length); - } - @Override public ByteBuf readSlice(int length) { return new UnreleasableByteBuf(buf.readSlice(length)); } - @Override - public ByteBuf readBytes(ByteBuf dst) { - buf.readBytes(dst); - return this; - } - - @Override - public ByteBuf readBytes(ByteBuf dst, int length) { - buf.readBytes(dst, length); - return this; - } - - @Override - public ByteBuf readBytes(ByteBuf dst, int dstIndex, int length) { - buf.readBytes(dst, dstIndex, length); - return this; - } - - @Override - public ByteBuf readBytes(byte[] dst) { - buf.readBytes(dst); - return this; - } - - @Override - public ByteBuf readBytes(byte[] dst, int dstIndex, int length) { - buf.readBytes(dst, dstIndex, length); - return this; - } - - @Override - public ByteBuf readBytes(ByteBuffer dst) { - buf.readBytes(dst); - return this; - } - - @Override - public ByteBuf readBytes(OutputStream out, int length) throws IOException { - buf.readBytes(out, length); - return this; - } - - @Override - public int readBytes(GatheringByteChannel out, int length) throws IOException { - return buf.readBytes(out, length); - } - - @Override - public ByteBuf skipBytes(int length) { - buf.skipBytes(length); - return this; - } - - @Override - public ByteBuf writeBoolean(boolean value) { - buf.writeBoolean(value); - return this; - } - - @Override - public ByteBuf writeByte(int value) { - buf.writeByte(value); - return this; - } - - @Override - public ByteBuf writeShort(int value) { - buf.writeShort(value); - return this; - } - - @Override - public ByteBuf writeMedium(int value) { - buf.writeMedium(value); - return this; - } - - @Override - public ByteBuf writeInt(int value) { - buf.writeInt(value); - return this; - } - - @Override - public ByteBuf writeLong(long value) { - buf.writeLong(value); - return this; - } - - @Override - public ByteBuf writeChar(int value) { - buf.writeChar(value); - return this; - } - - @Override - public ByteBuf writeFloat(float value) { - buf.writeFloat(value); - return this; - } - - @Override - public ByteBuf writeDouble(double value) { - buf.writeDouble(value); - return this; - } - - @Override - public ByteBuf writeBytes(ByteBuf src) { - buf.writeBytes(src); - return this; - } - - @Override - public ByteBuf writeBytes(ByteBuf src, int length) { - buf.writeBytes(src, length); - return this; - } - - @Override - public ByteBuf writeBytes(ByteBuf src, int srcIndex, int length) { - buf.writeBytes(src, srcIndex, length); - return this; - } - - @Override - public ByteBuf writeBytes(byte[] src) { - buf.writeBytes(src); - return this; - } - - @Override - public ByteBuf writeBytes(byte[] src, int srcIndex, int length) { - buf.writeBytes(src, srcIndex, length); - return this; - } - - @Override - public ByteBuf writeBytes(ByteBuffer src) { - buf.writeBytes(src); - return this; - } - - @Override - public int writeBytes(InputStream in, int length) throws IOException { - return buf.writeBytes(in, length); - } - - @Override - public int writeBytes(ScatteringByteChannel in, int length) throws IOException { - return buf.writeBytes(in, length); - } - - @Override - public ByteBuf writeZero(int length) { - buf.writeZero(length); - return this; - } - - @Override - public int indexOf(int fromIndex, int toIndex, byte value) { - return buf.indexOf(fromIndex, toIndex, value); - } - - @Override - public int bytesBefore(byte value) { - return buf.bytesBefore(value); - } - - @Override - public int bytesBefore(int length, byte value) { - return buf.bytesBefore(length, value); - } - - @Override - public int bytesBefore(int index, int length, byte value) { - return buf.bytesBefore(index, length, value); - } - - @Override - public int forEachByte(ByteBufProcessor processor) { - return buf.forEachByte(processor); - } - - @Override - public int forEachByte(int index, int length, ByteBufProcessor processor) { - return buf.forEachByte(index, length, processor); - } - - @Override - public int forEachByteDesc(ByteBufProcessor processor) { - return buf.forEachByteDesc(processor); - } - - @Override - public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { - return buf.forEachByteDesc(index, length, processor); - } - - @Override - public ByteBuf copy() { - return buf.copy(); - } - - @Override - public ByteBuf copy(int index, int length) { - return buf.copy(index, length); - } - @Override public ByteBuf slice() { return new UnreleasableByteBuf(buf.slice()); @@ -727,81 +65,6 @@ final class UnreleasableByteBuf extends ByteBuf { return new UnreleasableByteBuf(buf.duplicate()); } - @Override - public int nioBufferCount() { - return buf.nioBufferCount(); - } - - @Override - public ByteBuffer nioBuffer() { - return buf.nioBuffer(); - } - - @Override - public ByteBuffer nioBuffer(int index, int length) { - return buf.nioBuffer(index, length); - } - - @Override - public ByteBuffer[] nioBuffers() { - return buf.nioBuffers(); - } - - @Override - public ByteBuffer[] nioBuffers(int index, int length) { - return buf.nioBuffers(index, length); - } - - @Override - public ByteBuffer internalNioBuffer(int index, int length) { - return buf.internalNioBuffer(index, length); - } - - @Override - public boolean hasArray() { - return buf.hasArray(); - } - - @Override - public byte[] array() { - return buf.array(); - } - - @Override - public int arrayOffset() { - return buf.arrayOffset(); - } - - @Override - public String toString(Charset charset) { - return buf.toString(charset); - } - - @Override - public String toString(int index, int length, Charset charset) { - return buf.toString(index, length, charset); - } - - @Override - public int hashCode() { - return buf.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return buf.equals(obj); - } - - @Override - public int compareTo(ByteBuf buffer) { - return buf.compareTo(buffer); - } - - @Override - public String toString() { - return StringUtil.simpleClassName(this) + '(' + buf.toString() + ')'; - } - @Override public ByteBuf retain(int increment) { return this; @@ -812,21 +75,6 @@ final class UnreleasableByteBuf extends ByteBuf { return this; } - @Override - public boolean isReadable(int size) { - return buf.isReadable(size); - } - - @Override - public boolean isWritable(int size) { - return buf.isWritable(size); - } - - @Override - public int refCnt() { - return buf.refCnt(); - } - @Override public boolean release() { return false; diff --git a/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java new file mode 100644 index 0000000000..8faa1d065c --- /dev/null +++ b/buffer/src/main/java/io/netty/buffer/WrappedByteBuf.java @@ -0,0 +1,826 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.buffer; + +import io.netty.util.internal.StringUtil; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.channels.GatheringByteChannel; +import java.nio.channels.ScatteringByteChannel; +import java.nio.charset.Charset; + +public class WrappedByteBuf extends ByteBuf { + + protected final ByteBuf buf; + + protected WrappedByteBuf(ByteBuf buf) { + if (buf == null) { + throw new NullPointerException("buf"); + } + this.buf = buf; + } + + @Override + public boolean hasMemoryAddress() { + return buf.hasMemoryAddress(); + } + + @Override + public long memoryAddress() { + return buf.memoryAddress(); + } + + @Override + public int capacity() { + return buf.capacity(); + } + + @Override + public ByteBuf capacity(int newCapacity) { + buf.capacity(newCapacity); + return this; + } + + @Override + public int maxCapacity() { + return buf.maxCapacity(); + } + + @Override + public ByteBufAllocator alloc() { + return buf.alloc(); + } + + @Override + public ByteOrder order() { + return buf.order(); + } + + @Override + public ByteBuf order(ByteOrder endianness) { + return buf.order(endianness); + } + + @Override + public ByteBuf unwrap() { + return buf; + } + + @Override + public boolean isDirect() { + return buf.isDirect(); + } + + @Override + public int readerIndex() { + return buf.readerIndex(); + } + + @Override + public ByteBuf readerIndex(int readerIndex) { + buf.readerIndex(readerIndex); + return this; + } + + @Override + public int writerIndex() { + return buf.writerIndex(); + } + + @Override + public ByteBuf writerIndex(int writerIndex) { + buf.writerIndex(writerIndex); + return this; + } + + @Override + public ByteBuf setIndex(int readerIndex, int writerIndex) { + buf.setIndex(readerIndex, writerIndex); + return this; + } + + @Override + public int readableBytes() { + return buf.readableBytes(); + } + + @Override + public int writableBytes() { + return buf.writableBytes(); + } + + @Override + public int maxWritableBytes() { + return buf.maxWritableBytes(); + } + + @Override + public boolean isReadable() { + return buf.isReadable(); + } + + @Override + public boolean isWritable() { + return buf.isWritable(); + } + + @Override + public ByteBuf clear() { + buf.clear(); + return this; + } + + @Override + public ByteBuf markReaderIndex() { + buf.markReaderIndex(); + return this; + } + + @Override + public ByteBuf resetReaderIndex() { + buf.resetReaderIndex(); + return this; + } + + @Override + public ByteBuf markWriterIndex() { + buf.markWriterIndex(); + return this; + } + + @Override + public ByteBuf resetWriterIndex() { + buf.resetWriterIndex(); + return this; + } + + @Override + public ByteBuf discardReadBytes() { + buf.discardReadBytes(); + return this; + } + + @Override + public ByteBuf discardSomeReadBytes() { + buf.discardSomeReadBytes(); + return this; + } + + @Override + public ByteBuf ensureWritable(int minWritableBytes) { + buf.ensureWritable(minWritableBytes); + return this; + } + + @Override + public int ensureWritable(int minWritableBytes, boolean force) { + return buf.ensureWritable(minWritableBytes, force); + } + + @Override + public boolean getBoolean(int index) { + return buf.getBoolean(index); + } + + @Override + public byte getByte(int index) { + return buf.getByte(index); + } + + @Override + public short getUnsignedByte(int index) { + return buf.getUnsignedByte(index); + } + + @Override + public short getShort(int index) { + return buf.getShort(index); + } + + @Override + public int getUnsignedShort(int index) { + return buf.getUnsignedShort(index); + } + + @Override + public int getMedium(int index) { + return buf.getMedium(index); + } + + @Override + public int getUnsignedMedium(int index) { + return buf.getUnsignedMedium(index); + } + + @Override + public int getInt(int index) { + return buf.getInt(index); + } + + @Override + public long getUnsignedInt(int index) { + return buf.getUnsignedInt(index); + } + + @Override + public long getLong(int index) { + return buf.getLong(index); + } + + @Override + public char getChar(int index) { + return buf.getChar(index); + } + + @Override + public float getFloat(int index) { + return buf.getFloat(index); + } + + @Override + public double getDouble(int index) { + return buf.getDouble(index); + } + + @Override + public ByteBuf getBytes(int index, ByteBuf dst) { + buf.getBytes(index, dst); + return this; + } + + @Override + public ByteBuf getBytes(int index, ByteBuf dst, int length) { + buf.getBytes(index, dst, length); + return this; + } + + @Override + public ByteBuf getBytes(int index, ByteBuf dst, int dstIndex, int length) { + buf.getBytes(index, dst, dstIndex, length); + return this; + } + + @Override + public ByteBuf getBytes(int index, byte[] dst) { + buf.getBytes(index, dst); + return this; + } + + @Override + public ByteBuf getBytes(int index, byte[] dst, int dstIndex, int length) { + buf.getBytes(index, dst, dstIndex, length); + return this; + } + + @Override + public ByteBuf getBytes(int index, ByteBuffer dst) { + buf.getBytes(index, dst); + return this; + } + + @Override + public ByteBuf getBytes(int index, OutputStream out, int length) throws IOException { + buf.getBytes(index, out, length); + return this; + } + + @Override + public int getBytes(int index, GatheringByteChannel out, int length) throws IOException { + return buf.getBytes(index, out, length); + } + + @Override + public ByteBuf setBoolean(int index, boolean value) { + buf.setBoolean(index, value); + return this; + } + + @Override + public ByteBuf setByte(int index, int value) { + buf.setByte(index, value); + return this; + } + + @Override + public ByteBuf setShort(int index, int value) { + buf.setShort(index, value); + return this; + } + + @Override + public ByteBuf setMedium(int index, int value) { + buf.setMedium(index, value); + return this; + } + + @Override + public ByteBuf setInt(int index, int value) { + buf.setInt(index, value); + return this; + } + + @Override + public ByteBuf setLong(int index, long value) { + buf.setLong(index, value); + return this; + } + + @Override + public ByteBuf setChar(int index, int value) { + buf.setChar(index, value); + return this; + } + + @Override + public ByteBuf setFloat(int index, float value) { + buf.setFloat(index, value); + return this; + } + + @Override + public ByteBuf setDouble(int index, double value) { + buf.setDouble(index, value); + return this; + } + + @Override + public ByteBuf setBytes(int index, ByteBuf src) { + buf.setBytes(index, src); + return this; + } + + @Override + public ByteBuf setBytes(int index, ByteBuf src, int length) { + buf.setBytes(index, src, length); + return this; + } + + @Override + public ByteBuf setBytes(int index, ByteBuf src, int srcIndex, int length) { + buf.setBytes(index, src, srcIndex, length); + return this; + } + + @Override + public ByteBuf setBytes(int index, byte[] src) { + buf.setBytes(index, src); + return this; + } + + @Override + public ByteBuf setBytes(int index, byte[] src, int srcIndex, int length) { + buf.setBytes(index, src, srcIndex, length); + return this; + } + + @Override + public ByteBuf setBytes(int index, ByteBuffer src) { + buf.setBytes(index, src); + return this; + } + + @Override + public int setBytes(int index, InputStream in, int length) throws IOException { + return buf.setBytes(index, in, length); + } + + @Override + public int setBytes(int index, ScatteringByteChannel in, int length) throws IOException { + return buf.setBytes(index, in, length); + } + + @Override + public ByteBuf setZero(int index, int length) { + buf.setZero(index, length); + return this; + } + + @Override + public boolean readBoolean() { + return buf.readBoolean(); + } + + @Override + public byte readByte() { + return buf.readByte(); + } + + @Override + public short readUnsignedByte() { + return buf.readUnsignedByte(); + } + + @Override + public short readShort() { + return buf.readShort(); + } + + @Override + public int readUnsignedShort() { + return buf.readUnsignedShort(); + } + + @Override + public int readMedium() { + return buf.readMedium(); + } + + @Override + public int readUnsignedMedium() { + return buf.readUnsignedMedium(); + } + + @Override + public int readInt() { + return buf.readInt(); + } + + @Override + public long readUnsignedInt() { + return buf.readUnsignedInt(); + } + + @Override + public long readLong() { + return buf.readLong(); + } + + @Override + public char readChar() { + return buf.readChar(); + } + + @Override + public float readFloat() { + return buf.readFloat(); + } + + @Override + public double readDouble() { + return buf.readDouble(); + } + + @Override + public ByteBuf readBytes(int length) { + return buf.readBytes(length); + } + + @Override + public ByteBuf readSlice(int length) { + return buf.readSlice(length); + } + + @Override + public ByteBuf readBytes(ByteBuf dst) { + buf.readBytes(dst); + return this; + } + + @Override + public ByteBuf readBytes(ByteBuf dst, int length) { + buf.readBytes(dst, length); + return this; + } + + @Override + public ByteBuf readBytes(ByteBuf dst, int dstIndex, int length) { + buf.readBytes(dst, dstIndex, length); + return this; + } + + @Override + public ByteBuf readBytes(byte[] dst) { + buf.readBytes(dst); + return this; + } + + @Override + public ByteBuf readBytes(byte[] dst, int dstIndex, int length) { + buf.readBytes(dst, dstIndex, length); + return this; + } + + @Override + public ByteBuf readBytes(ByteBuffer dst) { + buf.readBytes(dst); + return this; + } + + @Override + public ByteBuf readBytes(OutputStream out, int length) throws IOException { + buf.readBytes(out, length); + return this; + } + + @Override + public int readBytes(GatheringByteChannel out, int length) throws IOException { + return buf.readBytes(out, length); + } + + @Override + public ByteBuf skipBytes(int length) { + buf.skipBytes(length); + return this; + } + + @Override + public ByteBuf writeBoolean(boolean value) { + buf.writeBoolean(value); + return this; + } + + @Override + public ByteBuf writeByte(int value) { + buf.writeByte(value); + return this; + } + + @Override + public ByteBuf writeShort(int value) { + buf.writeShort(value); + return this; + } + + @Override + public ByteBuf writeMedium(int value) { + buf.writeMedium(value); + return this; + } + + @Override + public ByteBuf writeInt(int value) { + buf.writeInt(value); + return this; + } + + @Override + public ByteBuf writeLong(long value) { + buf.writeLong(value); + return this; + } + + @Override + public ByteBuf writeChar(int value) { + buf.writeChar(value); + return this; + } + + @Override + public ByteBuf writeFloat(float value) { + buf.writeFloat(value); + return this; + } + + @Override + public ByteBuf writeDouble(double value) { + buf.writeDouble(value); + return this; + } + + @Override + public ByteBuf writeBytes(ByteBuf src) { + buf.writeBytes(src); + return this; + } + + @Override + public ByteBuf writeBytes(ByteBuf src, int length) { + buf.writeBytes(src, length); + return this; + } + + @Override + public ByteBuf writeBytes(ByteBuf src, int srcIndex, int length) { + buf.writeBytes(src, srcIndex, length); + return this; + } + + @Override + public ByteBuf writeBytes(byte[] src) { + buf.writeBytes(src); + return this; + } + + @Override + public ByteBuf writeBytes(byte[] src, int srcIndex, int length) { + buf.writeBytes(src, srcIndex, length); + return this; + } + + @Override + public ByteBuf writeBytes(ByteBuffer src) { + buf.writeBytes(src); + return this; + } + + @Override + public int writeBytes(InputStream in, int length) throws IOException { + return buf.writeBytes(in, length); + } + + @Override + public int writeBytes(ScatteringByteChannel in, int length) throws IOException { + return buf.writeBytes(in, length); + } + + @Override + public ByteBuf writeZero(int length) { + buf.writeZero(length); + return this; + } + + @Override + public int indexOf(int fromIndex, int toIndex, byte value) { + return buf.indexOf(fromIndex, toIndex, value); + } + + @Override + public int bytesBefore(byte value) { + return buf.bytesBefore(value); + } + + @Override + public int bytesBefore(int length, byte value) { + return buf.bytesBefore(length, value); + } + + @Override + public int bytesBefore(int index, int length, byte value) { + return buf.bytesBefore(index, length, value); + } + + @Override + public int forEachByte(ByteBufProcessor processor) { + return buf.forEachByte(processor); + } + + @Override + public int forEachByte(int index, int length, ByteBufProcessor processor) { + return buf.forEachByte(index, length, processor); + } + + @Override + public int forEachByteDesc(ByteBufProcessor processor) { + return buf.forEachByteDesc(processor); + } + + @Override + public int forEachByteDesc(int index, int length, ByteBufProcessor processor) { + return buf.forEachByteDesc(index, length, processor); + } + + @Override + public ByteBuf copy() { + return buf.copy(); + } + + @Override + public ByteBuf copy(int index, int length) { + return buf.copy(index, length); + } + + @Override + public ByteBuf slice() { + return buf.slice(); + } + + @Override + public ByteBuf slice(int index, int length) { + return buf.slice(index, length); + } + + @Override + public ByteBuf duplicate() { + return buf.duplicate(); + } + + @Override + public int nioBufferCount() { + return buf.nioBufferCount(); + } + + @Override + public ByteBuffer nioBuffer() { + return buf.nioBuffer(); + } + + @Override + public ByteBuffer nioBuffer(int index, int length) { + return buf.nioBuffer(index, length); + } + + @Override + public ByteBuffer[] nioBuffers() { + return buf.nioBuffers(); + } + + @Override + public ByteBuffer[] nioBuffers(int index, int length) { + return buf.nioBuffers(index, length); + } + + @Override + public ByteBuffer internalNioBuffer(int index, int length) { + return buf.internalNioBuffer(index, length); + } + + @Override + public boolean hasArray() { + return buf.hasArray(); + } + + @Override + public byte[] array() { + return buf.array(); + } + + @Override + public int arrayOffset() { + return buf.arrayOffset(); + } + + @Override + public String toString(Charset charset) { + return buf.toString(charset); + } + + @Override + public String toString(int index, int length, Charset charset) { + return buf.toString(index, length, charset); + } + + @Override + public int hashCode() { + return buf.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return buf.equals(obj); + } + + @Override + public int compareTo(ByteBuf buffer) { + return buf.compareTo(buffer); + } + + @Override + public String toString() { + return StringUtil.simpleClassName(this) + '(' + buf.toString() + ')'; + } + + @Override + public ByteBuf retain(int increment) { + buf.retain(increment); + return this; + } + + @Override + public ByteBuf retain() { + buf.retain(); + return this; + } + + @Override + public boolean isReadable(int size) { + return buf.isReadable(size); + } + + @Override + public boolean isWritable(int size) { + return buf.isWritable(size); + } + + @Override + public int refCnt() { + return buf.refCnt(); + } + + @Override + public boolean release() { + return buf.release(); + } + + @Override + public boolean release(int decrement) { + return buf.release(decrement); + } +} diff --git a/common/src/main/java/io/netty/util/ResourceLeak.java b/common/src/main/java/io/netty/util/ResourceLeak.java index efbf36c3e4..d45fc71f28 100644 --- a/common/src/main/java/io/netty/util/ResourceLeak.java +++ b/common/src/main/java/io/netty/util/ResourceLeak.java @@ -17,6 +17,12 @@ package io.netty.util; public interface ResourceLeak { + /** + * Records the caller's current stack trace so that the {@link ResourceLeakDetector} can tell where the leaked + * resource was accessed lastly. + */ + void record(); + /** * Close the leak so that {@link ResourceLeakDetector} does not warn about leaked resources. * diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/netty/util/ResourceLeakDetector.java index 653d393406..1a9ab6e837 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetector.java @@ -17,42 +17,86 @@ package io.netty.util; import io.netty.util.internal.PlatformDependent; -import io.netty.util.internal.StringUtil; import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.EnumSet; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; +import static io.netty.util.internal.StringUtil.*; + public final class ResourceLeakDetector { - private static boolean disabled; + private static final String PROP_LEVEL = "io.netty.leakDetectionLevel"; + private static final Level DEFAULT_LEVEL = Level.SIMPLE; + + /** + * Represents the level of resource leak detection. + */ + public enum Level { + /** + * Disables resource leak detection. + */ + DISABLED, + /** + * Enables simplistic sampling resource leak detection which reports there is a leak or not, + * at the cost of small overhead (default). + */ + SIMPLE, + /** + * Enables advanced sampling resource leak detection which reports where the leaked object was accessed + * recently at the cost of high overhead. + */ + ADVANCED, + /** + * Enables paranoid resource leak detection which reports where the leaked object was accessed recently, + * at the cost of the highest possible overhead (for testing purposes only). + */ + PARANOID + } + + private static Level level; private static final InternalLogger logger = InternalLoggerFactory.getInstance(ResourceLeakDetector.class); static { - final boolean DISABLED = SystemPropertyUtil.getBoolean("io.netty.noResourceLeakDetection", false); - logger.debug("-Dio.netty.noResourceLeakDetection: {}", DISABLED); - disabled = DISABLED; + String levelStr = SystemPropertyUtil.get(PROP_LEVEL, DEFAULT_LEVEL.name()).trim().toUpperCase(); + Level level = DEFAULT_LEVEL; + for (Level l: EnumSet.allOf(Level.class)) { + if (levelStr.equals(l.name()) || levelStr.equals(String.valueOf(l.ordinal()))) { + level = l; + } + } + + ResourceLeakDetector.level = level; + if (logger.isDebugEnabled()) { + logger.debug("-D{}: {}", PROP_LEVEL, level.name().toLowerCase()); + } } private static final int DEFAULT_SAMPLING_INTERVAL = 113; /** - * Enables or disabled the resource leak detection. + * Sets the resource leak detection level. */ - public static void setEnabled(boolean enabled) { - disabled = !enabled; + public static void setLevel(Level level) { + if (level == null) { + throw new NullPointerException("level"); + } + ResourceLeakDetector.level = level; } /** - * Returns {@code true} if resource leak detection is enabled. + * Returns the current resource leak detection level. */ - public static boolean isEnabled() { - return !disabled; + public static Level getLevel() { + return level; } /** the linked list of active resources */ @@ -60,7 +104,7 @@ public final class ResourceLeakDetector { private final DefaultResourceLeak tail = new DefaultResourceLeak(null); private final ReferenceQueue refQueue = new ReferenceQueue(); - private final ConcurrentMap reportedLeaks = PlatformDependent.newConcurrentHashMap(); + private final ConcurrentMap reportedLeaks = PlatformDependent.newConcurrentHashMap(); private final String resourceType; private final int samplingInterval; @@ -71,7 +115,7 @@ public final class ResourceLeakDetector { private long leakCheckCnt; public ResourceLeakDetector(Class resourceType) { - this(StringUtil.simpleClassName(resourceType)); + this(simpleClassName(resourceType)); } public ResourceLeakDetector(String resourceType) { @@ -79,7 +123,7 @@ public final class ResourceLeakDetector { } public ResourceLeakDetector(Class resourceType, int samplingInterval, long maxActive) { - this(StringUtil.simpleClassName(resourceType), samplingInterval, maxActive); + this(simpleClassName(resourceType), samplingInterval, maxActive); } public ResourceLeakDetector(String resourceType, int samplingInterval, long maxActive) { @@ -108,17 +152,26 @@ public final class ResourceLeakDetector { * @return the {@link ResourceLeak} or {@code null} */ public ResourceLeak open(T obj) { - if (disabled || leakCheckCnt ++ % samplingInterval != 0) { + Level level = ResourceLeakDetector.level; + if (level == Level.DISABLED) { return null; } - reportLeak(); - - return new DefaultResourceLeak(obj); + if (level.ordinal() < Level.PARANOID.ordinal()) { + if (leakCheckCnt ++ % samplingInterval == 0) { + reportLeak(level); + return new DefaultResourceLeak(obj); + } else { + return null; + } + } else { + reportLeak(level); + return new DefaultResourceLeak(obj); + } } - private void reportLeak() { - if (!logger.isWarnEnabled()) { + private void reportLeak(Level level) { + if (!logger.isErrorEnabled()) { for (;;) { @SuppressWarnings("unchecked") DefaultResourceLeak ref = (DefaultResourceLeak) refQueue.poll(); @@ -131,9 +184,9 @@ public final class ResourceLeakDetector { } // Report too many instances. + int samplingInterval = level == Level.PARANOID? 1 : this.samplingInterval; if (active * samplingInterval > maxActive && loggedTooManyActive.compareAndSet(false, true)) { - logger.warn( - "LEAK: You are creating too many " + resourceType + " instances. " + + logger.error("LEAK: You are creating too many " + resourceType + " instances. " + resourceType + " is a shared resource that must be reused across the JVM," + "so that only a few instances are created."); } @@ -152,18 +205,29 @@ public final class ResourceLeakDetector { continue; } - if (reportedLeaks.putIfAbsent(ref.exception, Boolean.TRUE) == null) { - logger.warn( - "LEAK: " + resourceType + " was GC'd before being released correctly. " + - "The following stack trace shows where the leaked object was created, " + - "rather than where you failed to release it.", ref.exception); + String records = ref.toString(); + if (reportedLeaks.putIfAbsent(records, Boolean.TRUE) == null) { + if (records.isEmpty()) { + logger.error("LEAK: {}.release() was not called before it's garbage-collected. " + + "Enable advanced leak reporting to find out where the leak occurred. " + + "To enable advanced leak reporting, " + + "specify the JVM option '-D{}={}' or call {}.setLevel()", + resourceType, PROP_LEVEL, Level.ADVANCED.name().toLowerCase(), simpleClassName(this)); + } else { + logger.error( + "LEAK: {}.release() was not called before it's garbage-collected.{}", + resourceType, records); + } } } } private final class DefaultResourceLeak extends PhantomReference implements ResourceLeak { - private final ResourceLeakException exception; + private static final int MAX_RECORDS = 4; + + private final String creationRecord; + private final Deque lastRecords = new ArrayDeque(); private final AtomicBoolean freed; private DefaultResourceLeak prev; private DefaultResourceLeak next; @@ -172,8 +236,12 @@ public final class ResourceLeakDetector { super(referent, referent != null? refQueue : null); if (referent != null) { - exception = new ResourceLeakException( - referent.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(referent))); + Level level = getLevel(); + if (level.ordinal() >= Level.ADVANCED.ordinal()) { + creationRecord = newRecord(); + } else { + creationRecord = null; + } // TODO: Use CAS to update the list. synchronized (head) { @@ -185,11 +253,28 @@ public final class ResourceLeakDetector { } freed = new AtomicBoolean(); } else { - exception = null; + creationRecord = null; freed = new AtomicBoolean(true); } } + @Override + public void record() { + if (creationRecord != null) { + String value = newRecord(); + + synchronized (lastRecords) { + int size = lastRecords.size(); + if (size == 0 || !lastRecords.getLast().equals(value)) { + lastRecords.add(value); + } + if (size > MAX_RECORDS) { + lastRecords.removeFirst(); + } + } + } + } + @Override public boolean close() { if (freed.compareAndSet(false, true)) { @@ -204,5 +289,54 @@ public final class ResourceLeakDetector { } return false; } + + public String toString() { + if (creationRecord == null) { + return ""; + } + + StringBuilder buf = new StringBuilder(16384); + int lastRecordCount = lastRecords.size(); + + buf.append(NEWLINE); + buf.append("Recent access records: "); + buf.append(lastRecordCount); + buf.append(NEWLINE); + + if (lastRecordCount > 0) { + String[] lastRecords = this.lastRecords.toArray(new String[lastRecordCount]); + for (int i = lastRecords.length - 1; i >= 0; i --) { + buf.append('#'); + buf.append(i + 1); + buf.append(':'); + buf.append(NEWLINE); + buf.append(lastRecords[i]); + } + } + + buf.append("Created at:"); + buf.append(NEWLINE); + buf.append(creationRecord); + buf.setLength(buf.length() - NEWLINE.length()); + + return buf.toString(); + } + } + + private static String newRecord() { + StringBuilder buf = new StringBuilder(4096); + StackTraceElement[] array = new Throwable().getStackTrace(); + int recordsToSkip = 3; + for (StackTraceElement e: array) { + if (recordsToSkip > 0) { + recordsToSkip --; + } else { + buf.append('\t'); + buf.append(e.toString()); + buf.append(NEWLINE); + } + } + + return buf.toString(); } } diff --git a/common/src/main/java/io/netty/util/ResourceLeakException.java b/common/src/main/java/io/netty/util/ResourceLeakException.java deleted file mode 100644 index a69238884b..0000000000 --- a/common/src/main/java/io/netty/util/ResourceLeakException.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.util; - -import java.util.Arrays; - -public class ResourceLeakException extends RuntimeException { - - private static final long serialVersionUID = 7186453858343358280L; - - private final StackTraceElement[] cachedStackTrace; - - public ResourceLeakException() { - cachedStackTrace = getStackTrace(); - } - - public ResourceLeakException(String message) { - super(message); - cachedStackTrace = getStackTrace(); - } - - public ResourceLeakException(String message, Throwable cause) { - super(message, cause); - cachedStackTrace = getStackTrace(); - } - - public ResourceLeakException(Throwable cause) { - super(cause); - cachedStackTrace = getStackTrace(); - } - - @Override - public int hashCode() { - StackTraceElement[] trace = cachedStackTrace; - int hashCode = 0; - for (StackTraceElement e: trace) { - hashCode = hashCode * 31 + e.hashCode(); - } - return hashCode; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof ResourceLeakException)) { - return false; - } - if (o == this) { - return true; - } - - return Arrays.equals(cachedStackTrace, ((ResourceLeakException) o).cachedStackTrace); - } -} diff --git a/pom.xml b/pom.xml index 85f8eb0f6a..6f131e4a37 100644 --- a/pom.xml +++ b/pom.xml @@ -67,6 +67,26 @@ + + + leak + + + -server + -dsa -da -ea:io.netty... + -XX:+AggressiveOpts + -XX:+TieredCompilation + -XX:+UseBiasedLocking + -XX:+UseFastAccessorMethods + -XX:+UseStringCache + -XX:+OptimizeStringConcat + -XX:+HeapDumpOnOutOfMemoryError + -Dio.netty.leakDetectionLevel=3 + + + + + UTF-8 UTF-8 @@ -83,7 +103,7 @@ -XX:+HeapDumpOnOutOfMemoryError - + common buffer