Netty ByteBuf释放时机

gzx0 2020-04-23

ByteBuf释放不当容易造成内存泄漏。

一般情况下,业务handler中使用到的ByteBuf可以分为两类,请求ByteBuf和响应ByteBuf。如下:

public class MyServerHandler extends SimpleChannelInboundHandler<ByteBuf>{
    
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        //服务器回送数据给客户端, 回送一个随机id ,
        ByteBuf responseByteBuf = Unpooled.copiedBuffer(UUID.randomUUID().toString() + " ", Charset.forName("utf-8"));//这里UnpooledHeapByteBuf
        ctx.writeAndFlush(responseByteBuf);
    }
}

msg即为请求ByteBuf,而responseByteBuf即为响应ByteBuf。

响应ByteBuf(如writeAndFlush(buf))由netty释放,例如在将HeapBuf转换为DirectBuf的时候释放。

protected final ByteBuf newDirectBuffer(ByteBuf buf) {
        final int readableBytes = buf.readableBytes();
        if (readableBytes == 0) {
            ReferenceCountUtil.safeRelease(buf);
            return Unpooled.EMPTY_BUFFER;
        }

        final ByteBufAllocator alloc = alloc();
        if (alloc.isDirectBufferPooled()) {
            ByteBuf directBuf = alloc.directBuffer(readableBytes);
            directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
            ReferenceCountUtil.safeRelease(buf);//将作为HeapByteBuf的buf引用计数-1,更新状态
            return directBuf;
        }

        final ByteBuf directBuf = ByteBufUtil.threadLocalDirectBuffer();
        if (directBuf != null) {
            directBuf.writeBytes(buf, buf.readerIndex(), readableBytes);
            ReferenceCountUtil.safeRelease(buf);
            return directBuf;
        }

        // Allocating and deallocating an unpooled direct buffer is very expensive; give up.
        return buf;
    }

请求ByteBuf,Handler继承SimpleChannelInboundHandler可释放;

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        boolean release = true;
        try {
            if (acceptInboundMessage(msg)) {
                @SuppressWarnings("unchecked")
                I imsg = (I) msg;
                channelRead0(ctx, imsg);//业务handler只需重写channelRead0
            } else {
                release = false;
                ctx.fireChannelRead(msg);
            }
        } finally {
            if (autoRelease && release) {
                ReferenceCountUtil.release(msg);//引用计数-1
            }
        }
    }

请求ByteBuf,fireChannelRead(),由TailContext释放。

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {onUnhandledInboundMessage(ctx, msg);}

    protected void onUnhandledInboundMessage(ChannelHandlerContext ctx, Object msg) {
        onUnhandledInboundMessage(msg);
        if (logger.isDebugEnabled()) {
            logger.debug("Discarded message pipeline : {}. Channel : {}.",
                         ctx.pipeline().names(), ctx.channel());
        }
    }

    protected void onUnhandledInboundMessage(Object msg) {
        try {
            logger.debug(
                    "Discarded inbound message {} that reached at the tail of the pipeline. " +
                            "Please check your pipeline configuration.", msg);
        } finally {
            ReferenceCountUtil.release(msg);//引用计数-1
        }
    }

对于请求ByteBuf,当业务handler没有继承SimpleChannelInboundHandler,没有fireChannelRead,也没有ReferenceCountUtil.release的话,这时就要考虑内存溢出的情况了。

所以应该时刻记着由最后一个使用的人释放(ReferenceCountUtil.release(byteBuf))。

相关推荐