Prevent memory leak when SimpleChannelPools are destroyed (#9760)

Motivation:

https://github.com/netty/netty/pull/9548 introduced a change that creates a new AttributeKey
for each SimpleChannelPool instance created. AttributeKeys are cached statically in a ConstantPool
by the AttributeKey.newInstance method. Because of this, creating a SimpleChannelPool instance will
allocate memory that will never be released, even after the SimpleChannelPool is closed.

Modifications:

This change goes back to a single AttributeKey per SimpleChannelPool, just using a more specific
name to reduce the chance of conflicts with user code.

Result:

No memory is leaked after a SimpleChannelPool instance is created and destroyed.
This commit is contained in:
Matthew Miller 2019-11-07 05:48:56 -08:00 committed by Norman Maurer
parent b381cb253a
commit 394e7f0287

View File

@ -39,8 +39,8 @@ import static io.netty.util.internal.ObjectUtil.*;
*
*/
public class SimpleChannelPool implements ChannelPool {
private final AttributeKey<SimpleChannelPool> poolKey = AttributeKey.newInstance("channelPool." +
System.identityHashCode(this));
private static final AttributeKey<SimpleChannelPool> POOL_KEY =
AttributeKey.newInstance("io.netty.channel.pool.SimpleChannelPool");
private final Deque<Channel> deque = PlatformDependent.newConcurrentDeque();
private final ChannelPoolHandler handler;
private final ChannelHealthChecker healthCheck;
@ -172,7 +172,7 @@ public class SimpleChannelPool implements ChannelPool {
if (ch == null) {
// No Channel left in the pool bootstrap a new Channel
Bootstrap bs = bootstrap.clone();
bs.attr(poolKey, this);
bs.attr(POOL_KEY, this);
ChannelFuture f = connectChannel(bs);
if (f.isDone()) {
notifyConnect(f, promise);
@ -238,7 +238,7 @@ public class SimpleChannelPool implements ChannelPool {
if (future.isSuccess()) {
if (future.getNow()) {
try {
ch.attr(poolKey).set(this);
ch.attr(POOL_KEY).set(this);
handler.channelAcquired(ch);
promise.setSuccess(ch);
} catch (Throwable cause) {
@ -294,7 +294,7 @@ public class SimpleChannelPool implements ChannelPool {
private void doReleaseChannel(Channel channel, Promise<Void> promise) {
assert channel.eventLoop().inEventLoop();
// Remove the POOL_KEY attribute from the Channel and check if it was acquired from this pool, if not fail.
if (channel.attr(poolKey).getAndSet(null) != this) {
if (channel.attr(POOL_KEY).getAndSet(null) != this) {
closeAndFail(channel,
// Better include a stacktrace here as this is an user error.
new IllegalArgumentException(
@ -359,7 +359,7 @@ public class SimpleChannelPool implements ChannelPool {
}
private void closeChannel(Channel channel) {
channel.attr(poolKey).getAndSet(null);
channel.attr(POOL_KEY).getAndSet(null);
channel.close();
}