Netty ByteBuf的回收

本文详细解析了ByteBuf在Netty中的三种回收策略:ChannelOutboundHandler尾部处理、ByteToMessageDecoder处理和手动管理。重点介绍了如何确保正确释放ByteBuf,以及推荐的处理方式如配合ByteToMessageDecoder和SimpleChannelInboundHandler。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下三种情况下对于ByteBuf的回收策略:

  1. ChannelOutboundHandler中write的ByteBuf,在pipeline中的tail handler将负责释放,详见AbstractChannelHandlerContext中的如下函数
    
        private void write(Object msg, boolean flush, ChannelPromise promise) {
            ObjectUtil.checkNotNull(msg, "msg");
            try {
                if (isNotValidPromise(promise, true)) {
                    ReferenceCountUtil.release(msg);
                    // cancelled
                    return;
                }
            } catch (RuntimeException e) {
                ReferenceCountUtil.release(msg);
                throw e;
            }
    
            final AbstractChannelHandlerContext next = findContextOutbound(flush ?
                    (MASK_WRITE | MASK_FLUSH) : MASK_WRITE);
            final Object m = pipeline.touch(msg, next);
            EventExecutor executor = next.executor();
            if (executor.inEventLoop()) {
                if (flush) {
                    next.invokeWriteAndFlush(m, promise);
                } else {
                    next.invokeWrite(m, promise);
                }
            } else {
                final WriteTask task = WriteTask.newInstance(next, m, promise, flush);
                if (!safeExecute(executor, task, promise, m, !flush)) {
                    // We failed to submit the WriteTask. We need to cancel it so we decrement the pending bytes
                    // and put it back in the Recycler for re-use later.
                    //
                    // See https://github.com/netty/netty/issues/8343.
                    task.cancel();
                }
            }
        }

  2. ChannelInboundHandlerAdapter中接收到的ByteBuf,由最后一个接收到ByteBuf的handler负责释放,例如ByteToMessageDecoder处理过后的ByteBuf,由于向下游传递的类型不为ByteBuf,在ByteToMessageDecoder中即进行释放
        final void decodeRemovalReentryProtection(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
                throws Exception {
            decodeState = STATE_CALLING_CHILD_DECODE;
            try {
                decode(ctx, in, out);
            } finally {
                boolean removePending = decodeState == STATE_HANDLER_REMOVED_PENDING;
                decodeState = STATE_INIT;
                if (removePending) {
                    fireChannelRead(ctx, out, out.size());
                    out.clear();
                    handlerRemoved(ctx);
                }
            }
        }
    
    
        @Override
        public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
            if (decodeState == STATE_CALLING_CHILD_DECODE) {
                decodeState = STATE_HANDLER_REMOVED_PENDING;
                return;
            }
            ByteBuf buf = cumulation;
            if (buf != null) {
                // Directly set this to null so we are sure we not access it in any other method here anymore.
                cumulation = null;
                numReads = 0;
                int readable = buf.readableBytes();
                if (readable > 0) {
                    ctx.fireChannelRead(buf);
                    ctx.fireChannelReadComplete();
                } else {
                    buf.release();
                }
            }
            handlerRemoved0(ctx);
        }

  3. SimpleChannelInboundHandler只对ByteBuf类型的数据进行回收

        @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);
                } else {
                    release = false;
                    ctx.fireChannelRead(msg);
                }
            } finally {
                if (autoRelease && release) {
                    ReferenceCountUtil.release(msg);
                }
            }
        }
    
    
        public static boolean release(Object msg) {
            if (msg instanceof ReferenceCounted) {
                return ((ReferenceCounted) msg).release();
            }
            return false;
        }

由此做出如下总结:

  1. 向外写数据时使用ByteBuf,保证能够在最后一个ChannelOutboundHandler时被回收,因为ReferenceCountUtil.release(msg)只回收实现了ReferenceCounted的类
  2. 业务处理handler使用非ByteBuf类型作为输入时,需要保证之前的ChannelInboundHandler将回收接收到的ByteBuf
  3. ByteToMessageDecoder会回收ByteBuf,将生成的消息向下游传递
  4. 如果自行申请了ByteBuf,需要在使用完成后手动回收

最佳实践

  • 使用ByteToMessageDecoder + SimpleChannelInboundHandler处理写入消息
  • 使用ByteBuf类型向外输出消息
  • 尽量避免手动申请ByteBuf

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值