使用Netty框架編程,最常見的報錯信息之一是:refCnt : 0别惦,decrememt : 1狈茉,那么如何解決這個錯誤呢?
問題分析:當(dāng)創(chuàng)建一個Bytebuf對象時掸掸,它的引用為1氯庆,每次調(diào)用ratain(),它的引用就會加1猾漫,每次調(diào)用一個release()就會減1点晴;如果引用為0,再次訪問這個Bytebuf對象時悯周,就會拋出refCnt : 0粒督,decrememt : 1 的報錯信息。
第一步禽翼,查看異常信息屠橄,注意紅框族跛,這是報錯的具體行數(shù):
第二步,查看代碼锐墙,發(fā)現(xiàn)在入口程序類中繼承了SimpleChannelInboundHandler類礁哄,如下圖所示:
@Service("entranceHandler")
@ChannelHandler.Sharable
public class EntranceHandler extends SimpleChannelInboundHandler<Object>{
private static Logger log = LoggerFactory.getLogger(EntranceHandler.class);
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("....首次連接的判斷....");
ByteBuf byteBuf = (ByteBuf) msg;
// 標(biāo)記一下當(dāng)前的readIndex的位置
byteBuf.markReaderIndex();
// 判斷包頭長度
if (byteBuf.readableBytes() < ProtoInstant.FILED_LEN) {// 不夠包頭
return;
}
// 讀取字頭
int head = CharacterConvert.byteToInt(byteBuf.readByte());
//重置讀索引
byteBuf.resetReaderIndex();
if (head == ProtoInstant.FIELD_HEAD) {
log.info("....節(jié)點間的/netty client的連接建立....");
//http請求響應(yīng)
ctx.channel().pipeline().remove("httpRequestDecoder");
ctx.channel().pipeline().remove("httpResponseEncoder");
//websocket連接的建立
ctx.channel().pipeline().remove("websocketConnectHandler");
//websocket編碼解碼
ctx.channel().pipeline().remove("binaryDeCoder");
ctx.channel().pipeline().remove("binaryEncoder");
} else {
log.info("....websocket連接建立....");
ctx.channel().pipeline().remove("bdeCoder");
ctx.channel().pipeline().remove("benCoder");
}
ctx.fireChannelRead(byteBuf);
//刪除首次連接通道,一個連接只用到一次
ctx.channel().pipeline().remove(this);
}
}
原來是這里報的錯溪北,查看SimpleChannelInboundHandler源碼發(fā)現(xiàn)桐绒,在EntranceHandler類重寫channelRead0()方法時,先走的父類的channelRead方法之拨,該方法中的finally中調(diào)用了ReferenceCountUtil類的release()方法茉继,所以還沒走到下一個handler處理器就報異常信息了。
第三蚀乔,解決方法烁竭;
既然refCnt為0了,那在入口方法中出站之前調(diào)用retain0方法吉挣,這個問題就解決了派撕;
在netty里面,retain()和release()方法都是成對使用的睬魂。
如果不繼承SimpleChannelInboundHandler這個類终吼,是不是就不需要再調(diào)用retain()方法呢?測試了一下汉买,果真如此衔峰,繼承ChannelInboundHandlerAdapter類,代碼運(yùn)行正常蛙粘,沒有再報refCnt : 0垫卤,decrememt : 1 異常信息。
在SimpleChannelInboundHandler類中出牧,有這樣一段說明:
繼承該類會調(diào)用release()方法穴肘;
如果下面還有handler處理器,請調(diào)用retain()方法舔痕;
Handler處理器评抚,釋放Bytebuf對象的三種辦法:
第一種方法,繼承ChannelInboundHandlerAdapter類伯复,調(diào)用 super.channelRead(ctx, msg) 方法傳遞給下一個handler處理器慨代,讓下一個處理器來釋放;
第二種方法啸如,在最后一個Handler處理器手動調(diào)用release()方法釋放侍匙;
第二種方法,最后一個Handler處理器繼承SimpleChannelInboundHandler類叮雳,讓該類幫你釋放想暗。
如果不是TailHandler(最后一個handler)妇汗,最好不要繼承SimpleChannelInboundHandler,不然在傳遞給下一個handler前還需要進(jìn)行retain()處理说莫。