【Netty学习】八、ByteBuf的自动释放

八、ByteBuf的自动释放

在入站处理时,Netty是何时自动创建入站的ByteBuf的呢?
查看Netty源代码,我们可以看到,Netty的Reactor反应器线程会在底层的Java NIO通道读数据时,也就是AbstractNioByteChannel.NioByteUnsafe.read()处,调用ByteBufAllocator方法,创建ByteBuf实例,从操作系统缓冲区把数据读取到Bytebuf实例中,然后调用pipeline.fireChannelRead(byteBuf)方法将读取到的数据包送入到入站处理流水线中。

再看看入站处理时,入站的ByteBuf是如何自动释放的。

方式一:TailHandler自动释放Netty

默认会在ChannelPipline通道流水线的最后添加一个TailHandler末尾处理器,它实现了默认的处理方法,在这些方法中会帮助完成ByteBuf内存释放的工作。
在默认情况下,如果每个InboundHandler入站处理器,把最初的ByteBuf数据包一路往下传,那么TailHandler末尾处理器会自动释放掉入站的ByteBuf实例。

如何让ByteBuf数据包通过流水线一路向后传递呢?
如果自定义的InboundHandler入站处理器继承自ChannelInboundHandlerAdapter适配器,那么可以在InboundHandler的入站处理方法中调用基类的入站处理方法,演示代码如下:

        public class DomoHandler extends ChannelInboundHandlerAdapter {

          /**
            * 出站处理方法
            * @param ctx上下文
            * @param msg  入站数据包
            * @throws Exception可能抛出的异常
            */
           @Override
           public void channelRead(ChannelHandlerContextctx, Object msg) throws
    Exception {
              ByteBuf byteBuf = (ByteBuf) msg;
              //...省略ByteBuf的业务处理
              //自动释放ByteBuf的方法:调用父类的入站方法,将msg向后传递
              // super.channelRead(ctx, msg);
           }
        }

总体来说,如果自定义的InboundHandler入站处理器继承自ChannelInboundHandlerAdapter适配器,那么可以调用以下两种方法来释放ByteBuf内存:
(1)手动释放ByteBuf。具体的方式为调用byteBuf.release()。
(2)调用父类的入站方法将msg向后传递,依赖后面的处理器释放ByteBuf。具体的方式为调用基类的入站处理方法super.channelRead(ctx, msg)。

        public class DomoHandler extends ChannelInboundHandlerAdapter {

          /**
            * 出站处理方法
            * @param ctx上下文
            * @param msg  入站数据包
            * @throws Exception  可能抛出的异常
            */
            @Override
            public void channelRead(ChannelHandlerContextctx, Object msg) throws
    Exception {
              ByteBuf byteBuf = (ByteBuf) msg;
              //...省略ByteBuf的业务处理

              //释放ByteBuf的两种方法
              // 方法一:手动释放ByteBuf
              byteBuf.release();

              //方法二:调用父类的入站方法,将msg向后传递
              // super.channelRead(ctx, msg);
            }
        }

方式二:SimpleChannelInboundHandler自动释放

如果Handler业务处理器需要截断流水线的处理流程,不将ByteBuf数据包送入后边的InboundHandler入站处理器,这时,流水线末端的TailHandler末尾处理器自动释放缓冲区的工作自然就失效了。
在这种场景下,Handler业务处理器有两种选择:

  • 手动释放ByteBuf实例。
  • 继承SimpleChannelInboundHandler,利用它的自动释放功能。
    这里,我们聚焦的是第二种选择:看看SimpleChannelInboundHandler是如何自动释放的。
    以入站读数据为例,Handler业务处理器必须继承自SimpleChannelInboundHandler基类。并且,业务处理器的代码必须移动到重写的channelRead0(ctx, msg)方法中。SimpleChannelInboundHandle类的channelRead等入站处理方法,会在调用完实际的channelRead0方法后,帮忙释放ByteBuf实例。
    部分源代码如下:
              public abstract class SimpleChannelInboundHandler<I> extends
      ChannelInboundHandlerAdapter
              {
                  //基类的入站方法
                  @Override
                public void channelRead(ChannelHandlerContextctx, 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) {
                            //释放ByteBuf
                            ReferenceCountUtil.release(msg);
                        }
                      }
                  }
              }

在Netty的SimpleChannelInboundHandler类的源代码中,执行完由子类继承的channelRead0()业务处理后,在finally语句代码段中,ByteBuf被释放了一次,如果ByteBuf计数器为零,将被彻底释放掉。

再看看出站处理时,Netty是何时释放出站的ByteBuf的呢?
出站缓冲区的自动释放方式:HeadHandler自动释放。在出站处理流程中,申请分配到的ByteBuf主要是通过HeadHandler完成自动释放的。
出站处理用到的Bytebuf缓冲区,一般是要发送的消息,通常由Handler业务处理器所申请而分配的。例如,在write出站写入通道时,通过调用ctx.writeAndFlush(Bytebufmsg), Bytebuf缓冲区进入出站处理的流水线。在每一个出站Handler业务处理器中的处理完成后,最后数据包(或消息)会来到出站的最后一棒HeadHandler,在数据输出完成后,Bytebuf会被释放一次,如果计数器为零,将被彻底释放掉。
在Netty开发中,必须密切关注Bytebuf缓冲区的释放,如果释放不及时,会造成Netty的内存泄露(Memory Leak),最终导致内存耗尽。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值