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)