zoukankan      html  css  js  c++  java
  • netty的异常分析 IllegalReferenceCountException refCnt: 0

    netty的异常 IllegalReferenceCountException refCnt: 0

    这是因为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);
        }
    }
    

      

    这个问题产生的最要原因是在第一次发送完心跳请求后,ctx.write 等一系列方法调用了ByteBuf的release方法,将其引用计数减为了0 导致的:

    我们主要看其方法栈中调用信息,得到一个结论,是每次调用ctx.write/writeAndFlush, pipeline.write/writeAndFlush , 等一系列方法时,被封装的ByteBuf对象的引用计数会减一。导致第二次使用同样对象的包装对象时,出现引用计数的问题。

    可以调用 echoMsg.refCnt(); 来获取当前引用计数值. 在 ctx.write(...) 前后加一行打印, 就可以发现, ctx.write(...) 完之后, 引用计数减少了1.

    解决

    • 如果不想创建新的数据, 则可以直接在原对象里调用 echoMsg.retain() 进行引用计数加1.例如:
        @Override
        protected void channelRead0(final ChannelHandlerContext ctx, final HttpContent msg) {
            System.out.println("收到" + msg);
            ByteBuf echoMsg = msg.content();
            echoMsg.retain();
            System.out.println(new String(ByteBufUtil.getBytes(echoMsg)));
            DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, echoMsg);
            response.headers().set("Content-Type", "text/plain");
            ctx.write(response).addListener(ChannelFutureListener.CLOSE);
        }
    

      

    即上面的 echoMsg.retain() 方法.

    • 构造 response 对象时, 不要复用 echoMsg 对象, 例如:
    DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.copiedBuffer(echoMsg));
    

      

    即, 使用 Unpooled.copiedBuffer(...) 来复制多一份内存数据~

    • 直接使用 ChannelInboundHandlerAdapter 自动手动处理释放, 以免像 SimpleChannelInboundHandler 那样导致多次释放引用计数对象~
    package hello.in;
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.ByteBufUtil;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    import io.netty.handler.codec.http.DefaultFullHttpResponse;
    import io.netty.handler.codec.http.HttpContent;
    import io.netty.handler.codec.http.HttpResponseStatus;
    import io.netty.handler.codec.http.HttpVersion;
    public class EchoHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
            if (msg instanceof HttpContent) {
                manual(ctx, (HttpContent) msg);
            }
        }
        protected void manual(final ChannelHandlerContext ctx, final HttpContent msg) {
            System.out.println("收到" + msg);
            ByteBuf echoMsg = msg.content();
            System.out.println(new String(ByteBufUtil.getBytes(echoMsg)));
            DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, echoMsg);
            response.headers().set("Content-Type", "text/plain");
            ctx.write(response).addListener(ChannelFutureListener.CLOSE);
        }
        @Override
        public void channelReadComplete(final ChannelHandlerContext ctx) {
            ctx.flush();
        }
        @Override
        public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }
    

      

  • 相关阅读:
    006-STM32+BC26基本控制篇-基础应用-域名申请SSL证书
    005-STM32+BC26基本控制篇-基础应用-域名备案
    004-STM32+BC26基本控制篇-基础应用-购买域名,配置域名解析
    003-STM32+BC26基本控制篇-基础应用-安装Web服务器软件Nginx(.Windows系统)
    002-STM32+BC26基本控制篇-基础应用-测试APP扫码绑定BC26模组并实现APP和开发板之间通过MQTT进行远程通信控制
    Spark实战(六)spark SQL + hive(Python版)
    Spark实战(五)spark streaming + flume(Python版)
    Spark实战(二)Spark常用算子
    Spark面试常见问题(一)--RDD基础
    Spark实战(三)本地连接远程Spark(Python环境)
  • 原文地址:https://www.cnblogs.com/panchanggui/p/9832432.html
Copyright © 2011-2022 走看看