How to release Pooled direct ByteBuf in netty when written after by ChannelHandlerContext?

601 views Asked by At

In my c/s project, c/s need to exchange fixed-size message with netty.
I use pooled direct buffer to buffer message.
Code just like below:

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf in = (ByteBuf) msg;
    try {
        ...
    } finally {
        ReferenceCountUtil.release(msg);
    }
    // alloc larger size of memory to increase memory usage
    ByteBuf buffer = PooledByteBufAllocator.DEFAULT.directBuffer(4* 1024 * 1024, 4* 1024 * 1024);
    // mock exchange message
    for (int i = 0; i < 1024 * 1024; i++) {
        buffer.writeInt(i);
    }
    // code 1
    // ctx.write(buffer);
    // ReferenceCountUtil.release(buffer);
    // code 2
    // ctx.write(buffer, ctx.newPromise().addListener(f -> ReferenceCountUtil.release(buffer)));
    // code 3
    // ctx.write(buffer);
}

As I know, I need to release ByteBuf.
Which is the right way to use buffer, code 1, code 2 or code 3?
Intuitively code 2 may be a good choice.
But code 2 occurs error,

i.n.util.concurrent.DefaultPromise - An exception was thrown by XXXServerHandler$$Lambda$73/0x000000080018ac40.operationComplete()
io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1

Thanks.

2

There are 2 answers

1
Ilker Ike kucuk On BEST ANSWER

You need to do the option 3 "code 3" The handler on your channel pipeline is already calling release, when it is done with the bytebuf.

2
Ferrybig On

Use a try-finally block for this:

// alloc larger size of memory to increase memory usage
ByteBuf buffer = PooledByteBufAllocator.DEFAULT.directBuffer(4* 1024 * 1024, 4* 1024 * 1024);
try {
    // mock exchange message
    for (int i = 0; i < 1024 * 1024; i++) {
        buffer.writeInt(i);
    }
    ctx.write(buffer.retain());
} finally {
    buffer.release();
}

When the buffer is acquired from the pool, it has a reference count of 1. You then do things with it, like filling, and if any exception happens here, the code goes into the finally part, releasing the buffer.

A special thing happens when calling a function that is going to continue to run after your function is done, like write. Releasing the buffer manually will be to soon, while it is hard to do safely, so we use the retain trick. We call the method retain on the buffer, so the reference count becomes 2, meaning it our call to release in the finally block will decrement it back to 1, and when write is done, it will be converted to 0 and given back to the pool