netty的异常分析 IllegalReferenceCountException refCnt: 0

 https://github.com/ZhuBaker/ant-rpc/blob/master/ant-remoting/src/main/java/com/antrpc/remoting/model/Heartbeats.java

package com.antrpc.remoting.model;

import com.antrpc.common.protocal.AntProtocal;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

/**
 * Created with IntelliJ IDEA.
 * Description: 自定义心跳协议
 * User: zhubo
 * Date: 2018-03-30
 * Time: 9:19
 */
public class Heartbeats {

    private static final ByteBuf HEARTBEAT_BUF;

    static {
        ByteBuf buf = Unpooled.buffer(AntProtocal.HEAD_LENGTH);
        buf.writeShort(AntProtocal.MAGIC);//2
        buf.writeByte(AntProtocal.HEARTBEAT);//1
        buf.writeByte(0);//1
        buf.writeLong(0);//8
        buf.writeInt(0);//4
        buf.writeByte(0);//1 compress
        HEARTBEAT_BUF = buf.asReadOnly();
    }
    /**
     * 返回公用的心跳内容.
     * @return
     */
    public static ByteBuf heartbeatContent(){
        //复制当前对象,复制后的对象与前对象共享缓冲区,且维护自己的独立索引
        return HEARTBEAT_BUF.duplicate();
    }
}

设计代码的初始目的是为了按照协议栈的格式 封装统一的心跳信息,来达到内存复用的目的。首先将ByteBuf中填充心跳数据,然后利用 装饰者模式 封装其为ReadOnlyByteBuf 对象并返回,对每次调用heartbeatContent 方法时,都返回一个共享内存空间,但是独立维护自己读写index索引的DuplicatedByteBuf对象。

我们开始设计初期的目的是没问题的,如果仅仅如此,这样设计就可以达到我们想要的效果。

但是当我们调用第二次 heartbeatContent() 获取 原对象封装的DuplicatedByteBuf对象候 对其调用

 ctx.writeAndFlush(Heartbeats.heartbeatContent());

会出现如下异常

io.netty.util.IllegalReferenceCountException: refCnt: 0
	at io.netty.buffer.AbstractByteBuf.ensureAccessible(AbstractByteBuf.java:1423) ~[netty-all-4.1.0.Final.jar:4.1.0.Final]
	at io.netty.buffer.UnpooledHeapByteBuf.capacity(UnpooledHeapByteBuf.java:102) ~[netty-all-4.1.0.Final.jar:4.1.0.Final]
	at io.netty.buffer.ReadOnlyByteBuf.capacity(ReadOnlyByteBuf.java:408) ~[netty-all-4.1.0.Final.jar:4.1.0.Final]
	at io.netty.buffer.AbstractByteBuf.setIndex(AbstractByteBuf.java:126) ~[netty-all-4.1.0.Final.jar:4.1.0.Final]
	at io.netty.buffer.ReadOnlyByteBuf.<init>(ReadOnlyByteBuf.java:50) ~[netty-all-4.1.0.Final.jar:4.1.0.Final]
	at io.netty.buffer.ReadOnlyByteBuf.duplicate(ReadOnlyByteBuf.java:278) ~[netty-all-4.1.0.Final.jar:4.1.0.Final]
下面为堆栈中自己写的程序代码抛出异常的地方
	at com.antrpc.remoting.model.Heartbeats.heartbeatContent(Heartbeats.java:33) ~[classes/:na]
	at com.antrpc.remoting.netty.idle.ConnectorIdleStateTrigger.userEventTriggered(ConnectorIdleStateTrigger.java:30) ~[classes/:na]

这是因为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对象的引用计数会减一。导致第二次使用同样对象的包装对象时,出现引用计数的问题。

package com.antrpc.remoting.model;

import com.antrpc.common.protocal.AntProtocal;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;

/**
 * Created with IntelliJ IDEA.
 * Description: 自定义心跳协议
 * User: zhubo
 * Date: 2018-03-30
 * Time: 9:19
 */
public class Heartbeats {

    private static final ByteBuf HEARTBEAT_BUF;

    static {
        ByteBuf buf = Unpooled.buffer(AntProtocal.HEAD_LENGTH);
        buf.writeShort(AntProtocal.MAGIC);//2  magic
        buf.writeByte(AntProtocal.HEARTBEAT);//1 code
        buf.writeByte(0);//1 type
        buf.writeLong(0);//8 id
        buf.writeInt(0);//4 length
        buf.writeByte(0);//1 compress
        HEARTBEAT_BUF = buf.asReadOnly();
    }
    /**
     * 返回公用的心跳内容.
     * @return
     */
    public static ByteBuf heartbeatContent(){

        // 我们可以调用ByteBuf.retain()将引用计数加1
        HEARTBEAT_BUF.retain();
        //复制当前对象,复制后的对象与前对象共享缓冲区,且维护自己的独立索引
        return HEARTBEAT_BUF.duplicate();
    }
}

参考文章:

用Netty开发中间件:高并发性能优化

https://blog.csdn.net/linuu/article/details/51141655

https://blog.csdn.net/jiangguilong2000/article/details/78602103

http://damacheng009.iteye.com/blog/2013657

http://ju.outofmemory.cn/entry/85894

https://blog.csdn.net/u010853261/article/details/53690780

https://www.cnblogs.com/zemliu/p/3667332.html

猜你喜欢

转载自my.oschina.net/LucasZhu/blog/1799075
今日推荐