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
This commit is contained in:
Trustin Lee 2013-12-04 19:03:32 +09:00
parent 6bba3c19dd
commit 6431be8954
17 changed files with 1822 additions and 907 deletions

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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<T> extends AbstractReferenceCountedByteBuf {
private final ResourceLeak leak;
private final Recycler.Handle recyclerHandle;
protected PoolChunk<T> chunk;
@ -38,7 +36,6 @@ abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
protected PooledByteBuf(Recycler.Handle recyclerHandle, int maxCapacity) {
super(maxCapacity);
leak = leakDetector.open(this);
this.recyclerHandle = recyclerHandle;
}
@ -144,13 +141,9 @@ abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
this.handle = -1;
memory = null;
chunk.arena.free(chunk, handle);
if (leak != null) {
leak.close();
} else {
recycle();
}
}
}
@SuppressWarnings("unchecked")
private void recycle() {

View File

@ -217,26 +217,34 @@ public class PooledByteBufAllocator extends AbstractByteBufAllocator {
protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
PoolThreadCache cache = threadCache.get();
PoolArena<byte[]> 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<ByteBuffer> 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

View File

@ -317,10 +317,14 @@ final class PooledUnsafeDirectByteBuf extends PooledByteBuf<ByteBuffer> {
@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);
if (copy.hasMemoryAddress()) {
PlatformDependent.copyMemory(addr(index), copy.memoryAddress(), length);
copy.setIndex(0, length);
} else {
copy.writeBytes(this, index, length);
}
}
return copy;
}

View File

@ -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) {

View File

@ -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);
if (copy.hasMemoryAddress()) {
PlatformDependent.copyMemory(addr(index), copy.memoryAddress(), length);
copy.setIndex(0, length);
} else {
copy.writeBytes(this, index, length);
}
}
return copy;
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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);
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

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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.
*

View File

@ -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<T> {
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<T> {
private final DefaultResourceLeak tail = new DefaultResourceLeak(null);
private final ReferenceQueue<Object> refQueue = new ReferenceQueue<Object>();
private final ConcurrentMap<Exception, Boolean> reportedLeaks = PlatformDependent.newConcurrentHashMap();
private final ConcurrentMap<String, Boolean> reportedLeaks = PlatformDependent.newConcurrentHashMap();
private final String resourceType;
private final int samplingInterval;
@ -71,7 +115,7 @@ public final class ResourceLeakDetector<T> {
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<T> {
}
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<T> {
* @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();
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<T> {
}
// 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<T> {
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<Object> implements ResourceLeak {
private final ResourceLeakException exception;
private static final int MAX_RECORDS = 4;
private final String creationRecord;
private final Deque<String> lastRecords = new ArrayDeque<String>();
private final AtomicBoolean freed;
private DefaultResourceLeak prev;
private DefaultResourceLeak next;
@ -172,8 +236,12 @@ public final class ResourceLeakDetector<T> {
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<T> {
}
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<T> {
}
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();
}
}

View File

@ -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);
}
}

20
pom.xml
View File

@ -67,6 +67,26 @@
</developer>
</developers>
<profiles>
<profile>
<id>leak</id>
<properties>
<test.jvm.argLine>
-server
-dsa -da -ea:io.netty...
-XX:+AggressiveOpts
-XX:+TieredCompilation
-XX:+UseBiasedLocking
-XX:+UseFastAccessorMethods
-XX:+UseStringCache
-XX:+OptimizeStringConcat
-XX:+HeapDumpOnOutOfMemoryError
-Dio.netty.leakDetectionLevel=3
</test.jvm.argLine>
</properties>
</profile>
</profiles>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>