#10226 improve ArrayByteBufferPool.Tracking

Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
Ludovic Orban 2023-08-23 11:06:34 +02:00
parent 2fcccef0c4
commit bbb994f96d
2 changed files with 34 additions and 17 deletions

View File

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

View File

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