#10226 improve ArrayByteBufferPool.Tracking
Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
parent
2fcccef0c4
commit
bbb994f96d
|
@ -85,9 +85,9 @@ public class HttpClientProxyProtocolTest
|
||||||
{
|
{
|
||||||
LifeCycle.stop(client);
|
LifeCycle.stop(client);
|
||||||
LifeCycle.stop(server);
|
LifeCycle.stop(server);
|
||||||
Set<ArrayByteBufferPool.Tracking.Buffer> serverLeaks = serverBufferPool.getLeaks();
|
Set<ArrayByteBufferPool.Tracking.TrackingBuffer> serverLeaks = serverBufferPool.getLeaks();
|
||||||
assertEquals(0, serverLeaks.size(), serverBufferPool.dumpLeaks());
|
assertEquals(0, serverLeaks.size(), serverBufferPool.dumpLeaks());
|
||||||
Set<ArrayByteBufferPool.Tracking.Buffer> clientLeaks = clientBufferPool.getLeaks();
|
Set<ArrayByteBufferPool.Tracking.TrackingBuffer> clientLeaks = clientBufferPool.getLeaks();
|
||||||
assertEquals(0, clientLeaks.size(), clientBufferPool.dumpLeaks());
|
assertEquals(0, clientLeaks.size(), clientBufferPool.dumpLeaks());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,10 @@ import java.io.StringWriter;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntUnaryOperator;
|
import java.util.function.IntUnaryOperator;
|
||||||
|
@ -575,14 +577,14 @@ public class ArrayByteBufferPool implements ByteBufferPool, Dumpable
|
||||||
* <p>A variant of {@link ArrayByteBufferPool} that tracks buffer
|
* <p>A variant of {@link ArrayByteBufferPool} that tracks buffer
|
||||||
* acquires/releases, useful to identify buffer leaks.</p>
|
* acquires/releases, useful to identify buffer leaks.</p>
|
||||||
* <p>Use {@link #getLeaks()} when the system is idle to get
|
* <p>Use {@link #getLeaks()} when the system is idle to get
|
||||||
* the {@link Buffer}s that have been leaked, which contain
|
* the {@link TrackingBuffer}s that have been leaked, which contain
|
||||||
* the stack trace information of where the buffer was acquired.</p>
|
* the stack trace information of where the buffer was acquired.</p>
|
||||||
*/
|
*/
|
||||||
public static class Tracking extends ArrayByteBufferPool
|
public static class Tracking extends ArrayByteBufferPool
|
||||||
{
|
{
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(Tracking.class);
|
private static final Logger LOG = LoggerFactory.getLogger(Tracking.class);
|
||||||
|
|
||||||
private final Set<Buffer> buffers = ConcurrentHashMap.newKeySet();
|
private final Set<TrackingBuffer> buffers = ConcurrentHashMap.newKeySet();
|
||||||
|
|
||||||
public Tracking()
|
public Tracking()
|
||||||
{
|
{
|
||||||
|
@ -603,14 +605,14 @@ public class ArrayByteBufferPool implements ByteBufferPool, Dumpable
|
||||||
public RetainableByteBuffer acquire(int size, boolean direct)
|
public RetainableByteBuffer acquire(int size, boolean direct)
|
||||||
{
|
{
|
||||||
RetainableByteBuffer buffer = super.acquire(size, direct);
|
RetainableByteBuffer buffer = super.acquire(size, direct);
|
||||||
Buffer wrapper = new Buffer(buffer, size);
|
TrackingBuffer wrapper = new TrackingBuffer(buffer, size);
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("acquired {}", wrapper);
|
LOG.debug("acquired {}", wrapper);
|
||||||
buffers.add(wrapper);
|
buffers.add(wrapper);
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Buffer> getLeaks()
|
public Set<TrackingBuffer> getLeaks()
|
||||||
{
|
{
|
||||||
return buffers;
|
return buffers;
|
||||||
}
|
}
|
||||||
|
@ -618,17 +620,19 @@ public class ArrayByteBufferPool implements ByteBufferPool, Dumpable
|
||||||
public String dumpLeaks()
|
public String dumpLeaks()
|
||||||
{
|
{
|
||||||
return getLeaks().stream()
|
return getLeaks().stream()
|
||||||
.map(Buffer::dump)
|
.map(TrackingBuffer::dump)
|
||||||
.collect(Collectors.joining(System.lineSeparator()));
|
.collect(Collectors.joining(System.lineSeparator()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Buffer extends RetainableByteBuffer.Wrapper
|
public class TrackingBuffer extends RetainableByteBuffer.Wrapper
|
||||||
{
|
{
|
||||||
private final int size;
|
private final int size;
|
||||||
private final Instant acquireInstant;
|
private final Instant acquireInstant;
|
||||||
private final Throwable acquireStack;
|
private final Throwable acquireStack;
|
||||||
|
private final List<Throwable> retainStacks = new CopyOnWriteArrayList<>();
|
||||||
|
private final List<Throwable> releaseStacks = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
private Buffer(RetainableByteBuffer wrapped, int size)
|
private TrackingBuffer(RetainableByteBuffer wrapped, int size)
|
||||||
{
|
{
|
||||||
super(wrapped);
|
super(wrapped);
|
||||||
this.size = size;
|
this.size = size;
|
||||||
|
@ -651,6 +655,13 @@ public class ArrayByteBufferPool implements ByteBufferPool, Dumpable
|
||||||
return acquireStack;
|
return acquireStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void retain()
|
||||||
|
{
|
||||||
|
super.retain();
|
||||||
|
retainStacks.add(new Throwable());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean release()
|
public boolean release()
|
||||||
{
|
{
|
||||||
|
@ -661,20 +672,26 @@ public class ArrayByteBufferPool implements ByteBufferPool, Dumpable
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("released {}", this);
|
LOG.debug("released {}", this);
|
||||||
}
|
}
|
||||||
|
releaseStacks.add(new Throwable());
|
||||||
return released;
|
return released;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String dump()
|
public String dump()
|
||||||
{
|
{
|
||||||
StringWriter w = new StringWriter();
|
StringWriter w = new StringWriter();
|
||||||
getAcquireStack().printStackTrace(new PrintWriter(w));
|
PrintWriter pw = new PrintWriter(w);
|
||||||
return "%s of %d bytes on %s at %s".formatted(getClass().getSimpleName(), getSize(), getAcquireInstant(), w);
|
getAcquireStack().printStackTrace(pw);
|
||||||
}
|
pw.println("\n" + retainStacks.size() + " retain(s)");
|
||||||
|
for (Throwable retainStack : retainStacks)
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
{
|
||||||
return "%s@%x[%s]".formatted(getClass().getSimpleName(), hashCode(), super.toString());
|
retainStack.printStackTrace(pw);
|
||||||
|
}
|
||||||
|
pw.println("\n" + releaseStacks.size() + " release(s)");
|
||||||
|
for (Throwable releaseStack : releaseStacks)
|
||||||
|
{
|
||||||
|
releaseStack.printStackTrace(pw);
|
||||||
|
}
|
||||||
|
return "%s@%x of %d bytes on %s acquired at %s".formatted(getClass().getSimpleName(), hashCode(), getSize(), getAcquireInstant(), w);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue