目录
- 说明: - 上图中,TailHandler 写成了TailContext,这个是没有错的。 - 对于流水线的头部和尾部Hander来说, Context和Hander ,是同一个类。 - HeadContext 与HeadHandler ,也是同一个类。 - 关于Context与Handler 的关系,请看 疯狂创客圈 的系列文章。 -  如果没有到达末端呢? -  一种没有到达入站处理流水线pipeline末端的情况,如下图所示:  -  这种场景下,也有一种自动释放的解决办法,它就是: -  可以继承 SimpleChannelInboundHandler,实现业务Handler。 SimpleChannelInboundHandler 会完成ByteBuf 的自动释放,释放的处理工作,在其入站处理方法 channelRead 中。 - 方式二:SimpleChannelInboundHandler 自动释放-  -  如果业务Handler需要将 ChannelPipleline的流水线的默认处理流程截断,不进行后边的inbound入站处理操作,这时候末端 TailHandler自动释放缓冲区的工作,自然就失效了。 -  这种场景下,业务Handler 有两种选择: -     手动释放 ByteBuf 实例 
-     继承 SimpleChannelInboundHandler,利用它的自动释放功能。 
 - 本小节,我们聚焦的是第二种选择:看看 SimpleChannelInboundHandler是如何自动释放的。 - 利用这种方法,业务处理Handler 必须继承 SimpleChannelInboundHandler基类。并且,业务处理的代码,必须 移动到 重写的 channelRead0(ctx, msg)方法中。 - 如果好奇,想看看 SimpleChannelInboundHandler 是如何释放ByteBuf 的,那就一起来看看Netty源码。 - 截取的代码如下所示: - public abstract class SimpleChannelInboundHandler<I> extends ChannelInboundHandlerAdapter { //... @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); } } }- 源码中,执行完重写的channelRead0()后,在 finally 语句块中,ByteBuf 的生命被结束掉了。 - 上面两种,都是入站处理(inbound)过程中的自动释放。 - 出站处理(outbound)流程,又是如何自动释放呢? - 方式三:HeadHandler 自动释放- 出站处理流程中,申请分配到的 ByteBuf,通过 HeadHandler 完成自动释放。 - 出站处理用到的 Bytebuf 缓冲区,一般是要发送的消息,通常由应用所申请。在出站流程开始的时候,通过调用 ctx.writeAndFlush(msg),Bytebuf 缓冲区开始进入出站处理的 pipeline 流水线 。在每一个出站Handler中的处理完成后,最后消息会来到出站的最后一棒 HeadHandler,再经过一轮复杂的调用,在flush完成后终将被release掉。  - 强调一下,HeadContext (HeadHandler)是出站处理流程的最后一棒。 - 出站处理的全过程,请查看疯狂创客圈的专门文章。 - 如何避免内存泄露-  基本上,在 Netty的开发中,通过 ChannelHandlerContext 或 Channel 获取的缓冲区ByteBuf 默认都是Pooled,所以需要再合适的时机对其进行释放,避免造成内存泄漏。 - 自动释放的注意事项- 我们已经知道了三种自动释放方法: -     通过 TailHandler 自动释放入站 ByteBuf 
-     继承 SimpleChannelInboundHandler 的完成 入站ByteBuf 自动释放 
-     通过HeadHandler自动释放出站 ByteBuf 自动释放,注意事项如下: 
-     入站处理流程中,如果对原消息不做处理,默认会调用 ctx.fireChannelRead(msg) 把原消息往下传,由流水线最后一棒 TailHandler 完成自动释放。 
-     如果截断了入站处理流水线,则可以继承 SimpleChannelInboundHandler ,完成入站ByteBuf 自动释放。 
-     出站处理过程中,申请分配到的 ByteBuf,通过 HeadHandler 完成自动释放。 出站处理用到的 Bytebuf 缓冲区,一般是要发送的消息,通常由应用所申请。在出站流程开始的时候,通过调用 ctx.writeAndFlush(msg),Bytebuf 缓冲区开始进入出站处理的 pipeline 流水线 。在每一个出站Handler中的处理完成后,最后消息会来到出站的最后一棒 HeadHandler,再经过一轮复杂的调用,在flush完成后终将被release掉。 
 - 手动释放的注意事项
-     
