netty的异常分析 IllegalReferenceCountException refCnt: 0

在下面代码中

public class EchoServerHandler extends SimpleChannelInboundHandler {
    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf) msg;
        System.out.println(
                "server receive: " + in.toString(CharsetUtil.UTF_8)
        );
        //此处回报异常io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
        ctx.write(in); 
    }


    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

在channelRead0()方法中的ctx.write(in),会报io.netty.util.IllegalReferenceCountException

原因是这是因为Netty有引用计数器的原因,自从Netty 4开始,对象的生命周期由它们的引用计数(reference counts)管理,而不是由垃圾收集器(garbage collector)管理了。
ByteBuf是最值得注意的,它使用了引用计数来改进分配内存和释放内存的性能。在我们创建ByteBuf对象后,它的引用计数是1,当你释放(release)引用计数对象时,它的引用计数减1,如果引用计数为0,这个引用计数对象会被释放(deallocate),并返回对象池。
当尝试访问引用计数为0的引用计数对象会抛出IllegalReferenceCountException异常:

/**
 * Should be called by every method that tries to access the buffers content to check
 * if the buffer was released before.
 */
protected final void ensureAccessible() {
    if (checkAccessible && refCnt() == 0) {
        throw new IllegalReferenceCountException(0);
    }
}

应用新建的Bytebuf返回数据,

public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
       ByteBuf in = (ByteBuf) msg;
       System.out.println(
               "server receive: " + in.toString(CharsetUtil.UTF_8)
       );
       String str = in.toString(CharsetUtil.UTF_8);
       /**
        *
        * ctx.write(in);
        * Bytebuf并不适合直接重用,必须新建才能写回客户端,
        * 否则将会报io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
        * 原因是这是因为Netty4有引用计数器的原因,自从Netty 4开始,对象的生命周期由它们的引用计数(reference counts)管理,
        * 而不是由垃圾收集器(garbage collector)管理了。ByteBuf是最值得注意的,它使用了引用计数来改进分配内存和释放内存的性能。
        * 在我们创建ByteBuf对象后,它的引用计数是1,当你释放(release)引用计数对象时,它的引用计数减1,
        * 如果引用计数为0,这个引用计数对象会被释放(deallocate),并返回对象池。
        * 当尝试访问引用计数为0的引用计数对象会抛出IllegalReferenceCountException异常:
        * 或者在该Bytebuf 在复用前需要调用retain(),将计数器置为1
        */
       ByteBuf pong = Unpooled.copiedBuffer(str.getBytes());
       ctx.write(pong);
   }

或者在复用前调用retain()方法

  public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf) msg;
        System.out.println(
                "server receive: " + in.toString(CharsetUtil.UTF_8)
        );
        in.retain();
        ctx.write(in);
}

或者该方法继承ChannelInboundHandlerAdapter实现其channelRead(ChannelHandlerContext ctx, Object msg)方法并不会报错,原因是并未检验ByteBuf的计数器

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值