1. io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1
at io.netty.buffer.AbstractReferenceCountedByteBuf.release0(AbstractReferenceCountedByteBuf.java:101)
at io.netty.buffer.AbstractReferenceCountedByteBuf.release(AbstractReferenceCountedByteBuf.java:89)
at io.netty.util.ReferenceCountUtil.release(ReferenceCountUtil.java:84)
at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:112)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:134)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:644)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:579)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:496)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:458)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
at java.lang.Thread.run(Thread.java:745)
该异常原因是因为,一个对象被多次执行release。
出错代码分析:
自己写的handler
public class XDecoder extends SimpleChannelInboundHandler {
// 消息头长度
public final int BASE_LENGTH = XLetter.LENGTH;
private int headerIndex;
private int bodyIndex;
// 消息头数据
private byte[] header;
// 消息体数据
private byte[] body;
// 日志操作类
private Logger logger;
public XDecoder(Logger logger) {
super();
this.logger = logger;
this.header = new byte[BASE_LENGTH];
initReadNewMessage();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
try {
NettyChannel.channelMap.get(ctx.channel()).updateReadTime();
read(ctx, byteBuf);
} catch (Exception e) {
logger.warn("received a error message. ", e);
}
// 因为该配置下SimpleChannelInboundHandler的autoRelease是true,所以ChannelRead方法在调用ChannelRead0方法后会执行
// ReferenceCountUtil.release(byteBuf)。如果这里也释放一次会出现一个对象多次释放,就会报
// io.netty.util.IllegalReferenceCountException: refCnt: 0, decrement: 1 异常。
finally {
ReferenceCountUtil.release(byteBuf);
}
}
自己写的代码里有
ReferenceCountUtil.release(byteBuf);
这里释放了一次,然后我们看我们继承的父类中
public abstract class SimpleChannelInboundHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (acceptInboundMessage(msg)) {
@SuppressWarnings("unchecked")
I imsg = (I) msg;
channelRead0(ctx, imsg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (autoRelease && release) {
ReferenceCountUtil.release(msg);
}
}
}
/**
* Please keep in mind that this method will be renamed to
* {@code messageReceived(ChannelHandlerContext, I)} in 5.0.
*
* Is called for each message of type {@link I}.
*
* @param ctx the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler}
* belongs to
* @param msg the message to handle
* @throws Exception is thrown if an error occurred
*/
protected abstract void channelRead0(ChannelHandlerContext ctx, I msg) throws Exception;
父类的channelRead方法中在调用了channelRead0的时候,还会判断要不要执行
ReferenceCountUtil.release(byteBuf);
这是后通过代码查看到
public abstract class SimpleChannelInboundHandler extends ChannelInboundHandlerAdapter {
private final TypeParameterMatcher matcher;
private final boolean autoRelease;
/**
* see {@link #SimpleChannelInboundHandler(boolean)} with {@code true} as boolean parameter.
*/
protected SimpleChannelInboundHandler() {
this(true);
}
/**
* Create a new instance which will try to detect the types to match out of the type parameter of the class.
*
* @param autoRelease {@code true} if handled messages should be released automatically by passing them to
* {@link ReferenceCountUtil#release(Object)}.
*/
protected SimpleChannelInboundHandler(boolean autoRelease) {
matcher = TypeParameterMatcher.find(this, SimpleChannelInboundHandler.class, "I");
this.autoRelease = autoRelease;
}
我们使用的是父类的默认构造方法,所以这里的autoRealse为true的。就会出现同一个对象进行两次的release。
同样的道理,我们在
SimpleChannelInboundHandler
要判断是否要release外,在OutboundHandler中也要判断是否要release,而且要保证不会多次被release。